Skip to content

Commit

Permalink
fix(core): correctly await promises called before init (#734)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kelly Wallach authored May 3, 2024
2 parents 11d30e8 + b329e05 commit db5d484
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 4 deletions.
14 changes: 11 additions & 3 deletions packages/analytics-core/src/core-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export class AmplitudeCore implements CoreClient {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
timeline: Timeline;
protected q: CallableFunction[] = [];
protected dispatchQ: CallableFunction[] = [];
protected q: Array<CallableFunction | typeof returnWrapper> = [];
protected dispatchQ: Array<CallableFunction> = [];

constructor(name = '$default') {
this.timeline = new Timeline(this);
Expand All @@ -48,7 +48,15 @@ export class AmplitudeCore implements CoreClient {
const queuedFunctions = this[queueName];
this[queueName] = [];
for (const queuedFunction of queuedFunctions) {
await queuedFunction();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const val: ReturnType<typeof returnWrapper> | Promise<any> = queuedFunction();
if (val && 'promise' in val) {
await val.promise;
} else {
await val;
}
}
}

Expand Down
52 changes: 51 additions & 1 deletion packages/analytics-core/test/core-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,25 @@ import { Event, Plugin, Status } from '@amplitude/analytics-types';
import { AmplitudeCore, Identify, Revenue } from '../src/index';
import { CLIENT_NOT_INITIALIZED, OPT_OUT_MESSAGE } from '../src/messages';
import { useDefaultConfig } from './helpers/default';

async function runScheduleTimers() {
// eslint-disable-next-line @typescript-eslint/unbound-method
await new Promise(process.nextTick);
jest.runAllTimers();
}
describe('core-client', () => {
const success = { event: { event_type: 'sample' }, code: 200, message: Status.Success };
const badRequest = { event: { event_type: 'sample' }, code: 400, message: Status.Invalid };
const continueRequest = { event: { event_type: 'sample' }, code: 100, message: Status.Unknown };
const client = new AmplitudeCore();

beforeEach(() => {
jest.useFakeTimers({ doNotFake: ['nextTick'] });
});

afterEach(() => {
jest.useRealTimers();
});

describe('init', () => {
test('should call init', async () => {
expect(client.config).toBeUndefined();
Expand Down Expand Up @@ -106,6 +118,44 @@ describe('core-client', () => {
expect(register).toHaveBeenCalledTimes(1);
expect(deregister).toHaveBeenCalledTimes(1);
});

test('should queue promises in correct order', async () => {
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const client = new AmplitudeCore();
const register = jest.spyOn(client.timeline, 'register');
const setupMockResolve = Promise.resolve();
const setupMock = jest.fn().mockResolvedValue(setupMockResolve);
await client.add({
name: 'firstPlugin',
type: 'before',
setup: async () => {
await sleep(10);
setupMock('firstPlugin');
return;
},
execute: jest.fn(),
}).promise;
client.add({
name: 'secondPlugin',
type: 'before',
setup: async () => {
setupMock('secondPlugin');
return;
},
execute: jest.fn(),
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const initPromise = (client as any)._init(useDefaultConfig());
await runScheduleTimers();
await initPromise;
await setupMockResolve;
expect(register).toHaveBeenCalledTimes(2);
expect(setupMock).toHaveBeenCalledTimes(2);
expect(setupMock.mock.calls[0][0]).toEqual('firstPlugin');
expect(setupMock.mock.calls[1][0]).toEqual('secondPlugin');
});
});

describe('dispatchWithCallback', () => {
Expand Down

0 comments on commit db5d484

Please sign in to comment.