From 917454a932c670c0e5bf96c45d91fe7569d6ff50 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Mon, 17 Feb 2020 22:58:33 -0700 Subject: [PATCH 1/8] e2e tests: improve reliability of login test Previously the tests were a bit flakey, working some times and not others. In this patch we've added the `--runInBand` option to `jest` so that the tests run sequentially in the same process as the test runny. It may have been that `jest` was attempting to run the tests in parallel even though the nature of testing Electron/Spectron doesn't support this and so we would sometimes fail based on concurrency conflicts. A few new tests have been added in order to verify that the new updates are working and to demonstrate how we can start adding testable behaviors into the app. We still need to improve how the tests are organized and setup to minimize the noise in the code and isolate the tests. --- e2e/test.ts | 62 +++++++++++++++++++++++++++++++++------ lib/auth/index.tsx | 5 +++- lib/icon-button/index.tsx | 2 +- package.json | 2 +- 4 files changed, 59 insertions(+), 12 deletions(-) diff --git a/e2e/test.ts b/e2e/test.ts index 6025078ab..161ff3bf5 100644 --- a/e2e/test.ts +++ b/e2e/test.ts @@ -3,6 +3,9 @@ import path from 'path'; import rimraf from 'rimraf'; import { Application } from 'spectron'; +const TEST_USERNAME = process.env.TEST_USERNAME as string; +const TEST_PASSWORD = process.env.TEST_PASSWORD as string; + const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); const app: Application = new Application({ @@ -26,21 +29,62 @@ describe('E2E', () => { expect(app.isRunning()).toEqual(true); }); - test('logs in', async () => { + const usernameField = () => app.client.$('#login__field-username'); + const passwordField = () => app.client.$('#login__field-password'); + const loginButton = () => app.client.$('#login__login-button'); + + const enterLoginInfo = async ( + username: string, + password: string, + maxWait = 500 + ) => { await app.client.waitUntilWindowLoaded(); - app.client - .$('#login__field-username') - .setValue(process.env.TEST_USERNAME as string); - app.client - .$('#login__field-password') - .setValue(process.env.TEST_PASSWORD as string); + expect(usernameField().waitForExist(maxWait)).toBeTruthy(); + usernameField().setValue(username); + expect(passwordField().waitForExist(maxWait)).toBeTruthy(); + passwordField().setValue(password); + }; + test('login with wrong password fails', async () => { + await app.client.waitUntilWindowLoaded(); + await enterLoginInfo(TEST_USERNAME, `${TEST_PASSWORD}_wrong`); await wait(500); + expect(loginButton().waitForExist(500)).toBeTruthy(); + loginButton().click(); - app.client.$('#login__login-button').click(); + expect( + app.client.$('[data-error-name="invalid-credentials"]').waitForExist(5000) + ).toBeTruthy(); + }, 6000); + test('login with correct password logs in', async () => { await app.client.waitUntilWindowLoaded(); - await app.client.waitForExist('.note-list', 10000); + await enterLoginInfo(TEST_USERNAME, TEST_PASSWORD); + await wait(2000); // DDoS guard + expect(loginButton().waitForExist(500)).toBeTruthy(); + loginButton().click(); + + await app.client.waitUntilWindowLoaded(); + expect(app.client.$('.note-list').waitForExist(10000)).toBeTruthy(); + }, 20000); + + test('can create new note by clicking on new note button', async () => { + await app.client.waitUntilWindowLoaded(); + await enterLoginInfo(TEST_USERNAME, TEST_PASSWORD); + await wait(2000); // DDoS guard + expect(loginButton().waitForExist(500)).toBeTruthy(); + loginButton().click(); + + await app.client.waitUntilWindowLoaded(); + const newNoteButton = app.client.$('button[data-title="New Note"]'); + expect(newNoteButton.waitForExist(10000)).toBeTruthy(); + newNoteButton.click(); + + expect( + app.client + .$('.public-DraftEditor-content.focus-visible') + .waitForExist(2000) + ).toBeTruthy(); }, 20000); }); diff --git a/lib/auth/index.tsx b/lib/auth/index.tsx index 411236a4c..99bf624cb 100644 --- a/lib/auth/index.tsx +++ b/lib/auth/index.tsx @@ -71,7 +71,10 @@ export class Auth extends Component {

{buttonLabel}

{this.props.hasInvalidCredentials && ( -

+

Could not log in with the provided email address and password.

)} diff --git a/lib/icon-button/index.tsx b/lib/icon-button/index.tsx index 3d368ca6d..13c5bb980 100644 --- a/lib/icon-button/index.tsx +++ b/lib/icon-button/index.tsx @@ -8,7 +8,7 @@ export const IconButton = ({ icon, title, ...props }) => ( enterDelay={200} title={title} > - diff --git a/package.json b/package.json index bf56b7564..ae22b6489 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "scripts": { "dev": "make dev", "start": "make start NODE_ENV=development", - "test-e2e": "npx jest --config=./jest.config.e2e.js", + "test-e2e": "npx jest --config=./jest.config.e2e.js --runInBand", "test": "make test", "lint": "make lint", "format": "make format", From 3f19b987a259c185f2d22009b712a8d0aae320cf Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 20 Feb 2020 18:22:14 -0700 Subject: [PATCH 2/8] Properly wait for DOM nodes to appear This patch fixes two bugs related to `waitForExist()` that led to the tests passing when they should have failed. The first bug was a type error whereas `waitForExist()` returns a promise-like object and `expect(...waitForExist()).toBeTruthy()` would therefore always be true. This meant that we not only didn't wait for the elements to appear but also that our check never failed. This has been fixed by awaiting the return. This has been handled inside of a new test helper function to cut down on incidental noise. The second bug was that, despite the type signaures in the webdriverio type definitions, `app.client` and `app.client.$()` return different types, whereas `app.client` returns a `Client` and `$()` returns a "WebElement JSON Object" or something that _isn't_ the same as the docs suggest. This bug was an infinite hang when trying to wait on the elements to appear since the returned value never resolved. Now all calls to wait on an element are handled by `app.client.waitForExist(selector, ...)` instead of using `$()` --- e2e/test.ts | 84 ++++++++++++++++++++-------------------------- lib/auth/index.tsx | 9 +++-- 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/e2e/test.ts b/e2e/test.ts index 161ff3bf5..3624e253a 100644 --- a/e2e/test.ts +++ b/e2e/test.ts @@ -6,85 +6,73 @@ import { Application } from 'spectron'; const TEST_USERNAME = process.env.TEST_USERNAME as string; const TEST_PASSWORD = process.env.TEST_PASSWORD as string; +const el = (app: Application, selector: string) => app.client.$(selector); const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); +const waitFor = async (app: Application, selector: string, msTimeout = 10000) => + expect(await app.client.waitForExist(selector, msTimeout)).toBe(true); const app: Application = new Application({ path: path.join(__dirname, '../node_modules/.bin/electron'), args: [path.join(__dirname, '..')], }); +let userData = ''; + beforeAll(async () => { await app.start(); - const userData = await app.electron.remote.app.getPath('userData'); + userData = await app.electron.remote.app.getPath('userData'); await app.stop(); - await new Promise(resolve => rimraf(userData, () => resolve())); - await app.start(); }, 10000); -afterAll(async () => app && app.isRunning() && (await app.stop())); - describe('E2E', () => { + beforeEach(async () => { + await new Promise(resolve => rimraf(userData, () => resolve())); + await app.start(); + }, 10000); + + afterEach(async () => app && app.isRunning() && (await app.stop())); + test('starts', async () => { await app.client.waitUntilWindowLoaded(); expect(app.isRunning()).toEqual(true); }); - const usernameField = () => app.client.$('#login__field-username'); - const passwordField = () => app.client.$('#login__field-password'); - const loginButton = () => app.client.$('#login__login-button'); + const usernameField = '#login__field-username'; + const passwordField = '#login__field-password'; + const loginButton = '#login__login-button'; - const enterLoginInfo = async ( - username: string, - password: string, - maxWait = 500 - ) => { - await app.client.waitUntilWindowLoaded(); + const loginWith = async (username: string, password: string) => { + await waitFor(app, usernameField); + el(app, usernameField).setValue(username); + await waitFor(app, passwordField); + el(app, passwordField).setValue(password); - expect(usernameField().waitForExist(maxWait)).toBeTruthy(); - usernameField().setValue(username); - expect(passwordField().waitForExist(maxWait)).toBeTruthy(); - passwordField().setValue(password); + await wait(2000); // try and prevent DDoS protection + await waitFor(app, loginButton); + el(app, loginButton).click(); }; test('login with wrong password fails', async () => { - await app.client.waitUntilWindowLoaded(); - await enterLoginInfo(TEST_USERNAME, `${TEST_PASSWORD}_wrong`); - await wait(500); - expect(loginButton().waitForExist(500)).toBeTruthy(); - loginButton().click(); + await loginWith(TEST_USERNAME, `${TEST_PASSWORD}_wrong`); - expect( - app.client.$('[data-error-name="invalid-credentials"]').waitForExist(5000) - ).toBeTruthy(); - }, 6000); + await waitFor(app, '[data-error-name="invalid-login"]'); + }, 20000); test('login with correct password logs in', async () => { await app.client.waitUntilWindowLoaded(); - await enterLoginInfo(TEST_USERNAME, TEST_PASSWORD); - await wait(2000); // DDoS guard - expect(loginButton().waitForExist(500)).toBeTruthy(); - loginButton().click(); + await loginWith(TEST_USERNAME, TEST_PASSWORD); - await app.client.waitUntilWindowLoaded(); - expect(app.client.$('.note-list').waitForExist(10000)).toBeTruthy(); + await waitFor(app, '.note-list'); }, 20000); test('can create new note by clicking on new note button', async () => { - await app.client.waitUntilWindowLoaded(); - await enterLoginInfo(TEST_USERNAME, TEST_PASSWORD); - await wait(2000); // DDoS guard - expect(loginButton().waitForExist(500)).toBeTruthy(); - loginButton().click(); + await loginWith(TEST_USERNAME, TEST_PASSWORD); + await wait(5000); // wait for notes to load - await app.client.waitUntilWindowLoaded(); - const newNoteButton = app.client.$('button[data-title="New Note"]'); - expect(newNoteButton.waitForExist(10000)).toBeTruthy(); - newNoteButton.click(); - - expect( - app.client - .$('.public-DraftEditor-content.focus-visible') - .waitForExist(2000) - ).toBeTruthy(); + const newNoteButton = 'button[data-title="New Note"]'; + await waitFor(app, newNoteButton); + el(app, newNoteButton).click(); + + await waitFor(app, '.public-DraftEditor-content.focus-visible'); }, 20000); }); diff --git a/lib/auth/index.tsx b/lib/auth/index.tsx index 99bf624cb..c519d900c 100644 --- a/lib/auth/index.tsx +++ b/lib/auth/index.tsx @@ -73,13 +73,18 @@ export class Auth extends Component { {this.props.hasInvalidCredentials && (

Could not log in with the provided email address and password.

)} {this.props.hasLoginError && ( -

{errorMessage}

+

+ {errorMessage} +

)} {passwordErrorMessage && (

From 66c8126258d9fd95663ced8f32cc4e3f92673bc1 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 20 Feb 2020 20:17:47 -0700 Subject: [PATCH 3/8] Create new account for testing instead of setting ENV vars --- e2e/test.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/e2e/test.ts b/e2e/test.ts index 3624e253a..5c164d632 100644 --- a/e2e/test.ts +++ b/e2e/test.ts @@ -1,10 +1,14 @@ import 'regenerator-runtime/runtime'; import path from 'path'; import rimraf from 'rimraf'; +import randomString from '../lib/utils/crypto-random-string'; import { Application } from 'spectron'; -const TEST_USERNAME = process.env.TEST_USERNAME as string; -const TEST_PASSWORD = process.env.TEST_PASSWORD as string; +const TEST_USERNAME = `sptest-${randomString(16)}@test.localhost.localdomain`; +const TEST_PASSWORD = randomString(22); +console.info( + `Creating user:\n email: ${TEST_USERNAME}\n password: ${TEST_PASSWORD}` +); const el = (app: Application, selector: string) => app.client.$(selector); const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); @@ -28,12 +32,12 @@ describe('E2E', () => { beforeEach(async () => { await new Promise(resolve => rimraf(userData, () => resolve())); await app.start(); + await app.client.waitUntilWindowLoaded(); }, 10000); afterEach(async () => app && app.isRunning() && (await app.stop())); test('starts', async () => { - await app.client.waitUntilWindowLoaded(); expect(app.isRunning()).toEqual(true); }); @@ -52,6 +56,17 @@ describe('E2E', () => { el(app, loginButton).click(); }; + test('creates an account', async () => { + await waitFor(app, '=Sign up'); + el(app, '=Sign up').click(); + + await waitFor(app, usernameField); + await loginWith(TEST_USERNAME, TEST_PASSWORD); + + await waitFor(app, '.note-list'); + await wait(5000); + }, 20000); + test('login with wrong password fails', async () => { await loginWith(TEST_USERNAME, `${TEST_PASSWORD}_wrong`); @@ -59,7 +74,6 @@ describe('E2E', () => { }, 20000); test('login with correct password logs in', async () => { - await app.client.waitUntilWindowLoaded(); await loginWith(TEST_USERNAME, TEST_PASSWORD); await waitFor(app, '.note-list'); From ac7336d0807f755db7011fa0a06034a64d39fa8c Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 20 Feb 2020 23:12:23 -0700 Subject: [PATCH 4/8] Eliminate timeouts by waiting for events --- e2e/test.ts | 34 ++++++++++++++++++++++++++++++---- lib/app.tsx | 4 ++++ webpack.config.js | 1 + 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/e2e/test.ts b/e2e/test.ts index 5c164d632..ceac0caf6 100644 --- a/e2e/test.ts +++ b/e2e/test.ts @@ -14,6 +14,31 @@ const el = (app: Application, selector: string) => app.client.$(selector); const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); const waitFor = async (app: Application, selector: string, msTimeout = 10000) => expect(await app.client.waitForExist(selector, msTimeout)).toBe(true); +const waitForWindowProp = async ( + app: Application, + propName: string, + msTimeout = 10000 +) => { + const tic = Date.now(); + + return new Promise(async (resolve, reject) => { + const f = async () => { + const result = await app.client.executeAsync(function(prop, done) { + return done(window[prop]); + }, propName); + + if (result.value) { + resolve(result.value); + } else if (Date.now() - tic < msTimeout) { + setTimeout(f, 100); + } else { + reject(); + } + }; + + await f(); + }); +}; const app: Application = new Application({ path: path.join(__dirname, '../node_modules/.bin/electron'), @@ -63,8 +88,8 @@ describe('E2E', () => { await waitFor(app, usernameField); await loginWith(TEST_USERNAME, TEST_PASSWORD); - await waitFor(app, '.note-list'); - await wait(5000); + await waitForWindowProp(app, 'testHasLoadedNotes'); + await wait(1000); // @TODO: This delay is necessary but shouldn't be }, 20000); test('login with wrong password fails', async () => { @@ -76,12 +101,13 @@ describe('E2E', () => { test('login with correct password logs in', async () => { await loginWith(TEST_USERNAME, TEST_PASSWORD); - await waitFor(app, '.note-list'); + await waitForWindowProp(app, 'testHasLoadedNotes'); }, 20000); test('can create new note by clicking on new note button', async () => { await loginWith(TEST_USERNAME, TEST_PASSWORD); - await wait(5000); // wait for notes to load + await waitForWindowProp(app, 'testHasLoadedNotes'); + await wait(1000); // @TODO: This delay is necessary but shouldn't be const newNoteButton = 'button[data-title="New Note"]'; await waitFor(app, newNoteButton); diff --git a/lib/app.tsx b/lib/app.tsx index 717262a51..46452a507 100644 --- a/lib/app.tsx +++ b/lib/app.tsx @@ -314,6 +314,10 @@ export const App = connect( loadNotes({ noteBucket }); setUnsyncedNoteIds(getUnsyncedNoteIds(noteBucket)); + + if ('test' === process.env.NODE_ENV) { + window.testHasLoadedNotes = true; + } }; onNoteRemoved = () => this.onNotesIndex(); diff --git a/webpack.config.js b/webpack.config.js index 686e8fd0c..f5be76d3a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -85,6 +85,7 @@ module.exports = () => { chunkFilename: isDevMode ? '[id].css' : '[id].[hash].css', }), new webpack.DefinePlugin({ + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), config: JSON.stringify(config), }), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), From 65e0452a2b9a6d99188af80df6c419d6781f455e Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Thu, 20 Feb 2020 23:56:52 -0700 Subject: [PATCH 5/8] Improve API for sending events to test runner --- e2e/test.ts | 44 ++++++++++++++++++++++++++----------- lib/app.tsx | 6 ++++- lib/boot.ts | 4 ++++ lib/note-content-editor.tsx | 16 +++++++++++--- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/e2e/test.ts b/e2e/test.ts index ceac0caf6..16e35f225 100644 --- a/e2e/test.ts +++ b/e2e/test.ts @@ -14,21 +14,39 @@ const el = (app: Application, selector: string) => app.client.$(selector); const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); const waitFor = async (app: Application, selector: string, msTimeout = 10000) => expect(await app.client.waitForExist(selector, msTimeout)).toBe(true); -const waitForWindowProp = async ( +const waitForEvent = async ( app: Application, - propName: string, + eventName: string, msTimeout = 10000 -) => { +): Promise => { const tic = Date.now(); return new Promise(async (resolve, reject) => { const f = async () => { - const result = await app.client.executeAsync(function(prop, done) { - return done(window[prop]); - }, propName); - - if (result.value) { - resolve(result.value); + const result = await app.client.execute(function() { + var events = window.testEvents; + + if (!events.length) { + return undefined; + } + + window.testEvents = []; + return events; + }); + + const firstOfType = + result.value && + result.value.findIndex( + (event: string | [string, ...any[]]) => + event === eventName || event[0] === eventName + ); + + if (result.value && firstOfType > -1) { + resolve( + 'string' === typeof result.value + ? [] + : result.value[firstOfType].slice(1) + ); } else if (Date.now() - tic < msTimeout) { setTimeout(f, 100); } else { @@ -88,7 +106,7 @@ describe('E2E', () => { await waitFor(app, usernameField); await loginWith(TEST_USERNAME, TEST_PASSWORD); - await waitForWindowProp(app, 'testHasLoadedNotes'); + await waitForEvent(app, 'notesLoaded'); await wait(1000); // @TODO: This delay is necessary but shouldn't be }, 20000); @@ -101,18 +119,18 @@ describe('E2E', () => { test('login with correct password logs in', async () => { await loginWith(TEST_USERNAME, TEST_PASSWORD); - await waitForWindowProp(app, 'testHasLoadedNotes'); + await waitForEvent(app, 'notesLoaded'); }, 20000); test('can create new note by clicking on new note button', async () => { await loginWith(TEST_USERNAME, TEST_PASSWORD); - await waitForWindowProp(app, 'testHasLoadedNotes'); + await waitForEvent(app, 'notesLoaded'); await wait(1000); // @TODO: This delay is necessary but shouldn't be const newNoteButton = 'button[data-title="New Note"]'; await waitFor(app, newNoteButton); el(app, newNoteButton).click(); - await waitFor(app, '.public-DraftEditor-content.focus-visible'); + await waitForEvent(app, 'editorNewNote'); }, 20000); }); diff --git a/lib/app.tsx b/lib/app.tsx index 46452a507..bd91a366e 100644 --- a/lib/app.tsx +++ b/lib/app.tsx @@ -208,6 +208,10 @@ export const App = connect( ); this.toggleShortcuts(true); + + if ('test' === process.env.NODE_ENV) { + window.testEvents.push('booted'); + } } componentWillUnmount() { @@ -316,7 +320,7 @@ export const App = connect( setUnsyncedNoteIds(getUnsyncedNoteIds(noteBucket)); if ('test' === process.env.NODE_ENV) { - window.testHasLoadedNotes = true; + window.testEvents.push('notesLoaded'); } }; diff --git a/lib/boot.ts b/lib/boot.ts index 2b8d76b4e..4add93151 100644 --- a/lib/boot.ts +++ b/lib/boot.ts @@ -1,3 +1,7 @@ +if ('test' === process.env.NODE_ENV) { + window.testEvents = []; +} + import './utils/ensure-platform-support'; import 'core-js/stable'; import 'regenerator-runtime/runtime'; diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index 6dd1adbb2..9b9ae8d3e 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -179,9 +179,19 @@ class NoteContentEditor extends Component { noteId !== prevProps.noteId || content.version !== prevProps.content.version ) { - this.setState({ - editorState: this.createNewEditorState(content.text, searchQuery), - }); + this.setState( + { + editorState: this.createNewEditorState(content.text, searchQuery), + }, + () => { + if ('test' === process.env.NODE_ENV) { + window.testEvents.push([ + 'editorNewNote', + plainTextContent(this.state.editorState), + ]); + } + } + ); return; } From 6f2783b4529bf31e610bb3ce7da1e4d082bcbca6 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 21 Feb 2020 18:37:30 -0700 Subject: [PATCH 6/8] Appease the linter --- e2e/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/test.ts b/e2e/test.ts index 16e35f225..3c899a309 100644 --- a/e2e/test.ts +++ b/e2e/test.ts @@ -54,7 +54,7 @@ const waitForEvent = async ( } }; - await f(); + f(); }); }; From f468a4435b6c17eefa275cc2d1e36f292cee1415 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 21 Feb 2020 19:06:52 -0700 Subject: [PATCH 7/8] Simplify test for "are we in a test" with new `__TEST__` macro --- lib/app.tsx | 8 ++------ lib/boot.ts | 2 +- lib/global.d.ts | 9 +++++++++ lib/note-content-editor.tsx | 14 ++++++-------- webpack.config.js | 2 +- 5 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 lib/global.d.ts diff --git a/lib/app.tsx b/lib/app.tsx index bd91a366e..71a61b348 100644 --- a/lib/app.tsx +++ b/lib/app.tsx @@ -209,9 +209,7 @@ export const App = connect( this.toggleShortcuts(true); - if ('test' === process.env.NODE_ENV) { - window.testEvents.push('booted'); - } + __TEST__ && window.testEvents.push('booted'); } componentWillUnmount() { @@ -319,9 +317,7 @@ export const App = connect( loadNotes({ noteBucket }); setUnsyncedNoteIds(getUnsyncedNoteIds(noteBucket)); - if ('test' === process.env.NODE_ENV) { - window.testEvents.push('notesLoaded'); - } + __TEST__ && window.testEvents.push('notesLoaded'); }; onNoteRemoved = () => this.onNotesIndex(); diff --git a/lib/boot.ts b/lib/boot.ts index 4add93151..21b77d745 100644 --- a/lib/boot.ts +++ b/lib/boot.ts @@ -1,4 +1,4 @@ -if ('test' === process.env.NODE_ENV) { +if (__TEST__) { window.testEvents = []; } diff --git a/lib/global.d.ts b/lib/global.d.ts new file mode 100644 index 000000000..5bfd6564d --- /dev/null +++ b/lib/global.d.ts @@ -0,0 +1,9 @@ +declare var __TEST__: boolean; + +interface Window { + _tkq: Array; + testEvents: (string | [string, ...any[]])[]; + wpcom: { + tracks: object; + }; +} diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index 9b9ae8d3e..8e26acd00 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -183,14 +183,12 @@ class NoteContentEditor extends Component { { editorState: this.createNewEditorState(content.text, searchQuery), }, - () => { - if ('test' === process.env.NODE_ENV) { - window.testEvents.push([ - 'editorNewNote', - plainTextContent(this.state.editorState), - ]); - } - } + () => + __TEST__ && + window.testEvents.push([ + 'editorNewNote', + plainTextContent(this.state.editorState), + ]) ); return; } diff --git a/webpack.config.js b/webpack.config.js index f5be76d3a..23615380e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -85,7 +85,7 @@ module.exports = () => { chunkFilename: isDevMode ? '[id].css' : '[id].[hash].css', }), new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), + __TEST__: JSON.stringify(process.env.NODE_ENV === 'test'), config: JSON.stringify(config), }), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), From b10762a50c3dcccccedfa8321693405b9bd7bddb Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Fri, 21 Feb 2020 19:28:12 -0700 Subject: [PATCH 8/8] Appease the linter --- e2e/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/test.ts b/e2e/test.ts index 3c899a309..b36b17de9 100644 --- a/e2e/test.ts +++ b/e2e/test.ts @@ -21,7 +21,7 @@ const waitForEvent = async ( ): Promise => { const tic = Date.now(); - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { const f = async () => { const result = await app.client.execute(function() { var events = window.testEvents;