Skip to content

Commit

Permalink
fix: communicate crash errors more clearly (#4376)
Browse files Browse the repository at this point in the history
  • Loading branch information
noomorph authored Feb 21, 2024
1 parent 2838b56 commit 30665df
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 3 deletions.
7 changes: 6 additions & 1 deletion detox/src/client/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ class Client {
}

async sendAction(action) {
if (this._pendingAppCrash) {
throw this._pendingAppCrash;
}

const { shouldQueryStatus, ...options } = this._inferSendOptions(action);

return await (shouldQueryStatus
Expand Down Expand Up @@ -302,6 +306,8 @@ class Client {
}

_onAppConnected() {
this._pendingAppCrash = null;

if (this._whenAppIsConnected.isPending()) {
this._whenAppIsConnected.resolve();
} else {
Expand Down Expand Up @@ -352,7 +358,6 @@ class Client {
if (this._pendingAppCrash) {
this._whenAppDisconnected.reject(this._pendingAppCrash);
this._asyncWebSocket.rejectAll(this._pendingAppCrash);
this._pendingAppCrash = null;
} else if (this._asyncWebSocket.hasPendingActions()) {
const error = new DetoxRuntimeError('The app has unexpectedly disconnected from Detox server.');
this._asyncWebSocket.rejectAll(error);
Expand Down
33 changes: 32 additions & 1 deletion detox/src/client/Client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,38 @@ describe('Client', () => {
mockAws.mockEventCallback('appDisconnected');
expect(mockAws.rejectAll.mock.calls[0][0]).toMatchSnapshot();
expect(log.error).not.toHaveBeenCalled();
await expect(client.waitUntilDisconnected()).rejects.toThrowError('SIGSEGV whatever');
});

describe('after app exits', () => {
beforeEach(async () => {
jest.spyOn(client, 'terminateApp');

await client.connect();

mockAws.mockEventCallback('AppWillTerminateWithError', {
params: { errorDetails: 'SIGSEGV whatever' },
});

jest.advanceTimersByTime(5000);
await fastForwardAllPromises();
mockAws.mockEventCallback('appDisconnected');
await fastForwardAllPromises();
});

it('should reject pending and future requests', async () => {
// any future requests should be rejected
mockAws.mockResponse('invokeResult', { result: 'some_result' });
await expect(client.sendAction(new actions.Invoke(anInvocation()))).rejects.toThrowError('SIGSEGV whatever');

// pending requests should be rejected
await expect(client.waitUntilDisconnected()).rejects.toThrowError('SIGSEGV whatever');
});

it('should allow new requests after the app reconnects', async () => {
mockAws.mockEventCallback('appConnected');
mockAws.mockResponse('invokeResult', { result: 'some_result' });
await expect(client.sendAction(new actions.Invoke(anInvocation()))).resolves.not.toThrow();
});
});

it('should log errors if the app termination does not go well', async () => {
Expand Down
15 changes: 14 additions & 1 deletion detox/test/e2e/19.crash-handling.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,27 @@ describe('Crash Handling', () => {
it('Should throw error upon internal app crash', async () => {
await device.reloadReactNative();
await expectToThrow(() => element(by.text('Crash')).tap(), 'The app has crashed');
await expectToThrow(() => element(by.text('Crash')).tap(), 'Detox can\'t seem to connect to the test app(s)!');
});

it('Should throw the same crash error even in the next test if the app was not relaunched', async () => {
await expectToThrow(() => element(by.text('Crash')).tap(), 'The app has crashed');
});

it('Should recover from app crash', async () => {
await device.launchApp({ newInstance: false });
await expect(element(by.text('Sanity'))).toBeVisible();
});

it('Should print generic connectivity error when the app was terminated intentionally', async () => {
/**
* @issue https://github.com/wix/Detox/issues/4377
* @tag flaky
*/
await device.terminateApp();
await new Promise((resolve) => setTimeout(resolve, 2000)); // see the issue for details
await expectToThrow(() => element(by.text('Crash')).tap(), 'Detox can\'t seem to connect to the test app(s)!');
});

it('Should throw a detailed error upon early app crash', async () => {
const error = await expectToThrow(
() => relaunchAppWithArgs({ simulateEarlyCrash: true }),
Expand Down

0 comments on commit 30665df

Please sign in to comment.