1// Copyright 2020 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import {assert} from 'chai';
6import * as puppeteer from 'puppeteer';
7
8import {$$, click, getBrowserAndPages, goToResource, step, timeout, waitFor, waitForFunction} from '../../shared/helper.js';
9import {describe, it} from '../../shared/mocha-extensions.js';
10import {addBreakpointForLine, createSelectorsForWorkerFile, getBreakpointDecorators, getExecutionLine, getOpenSources, openNestedWorkerFile, PAUSE_BUTTON, RESUME_BUTTON} from '../helpers/sources-helpers.js';
11
12async function validateSourceTabs() {
13  await step('Validate exactly one source file is open', async () => {
14    const openSources = await waitForFunction(async () => {
15      const sources = await getOpenSources();
16      return sources.length === 1 ? sources : undefined;
17    });
18    assert.deepEqual(openSources, ['multi-workers.js']);
19  });
20}
21
22
23describe('Multi-Workers', async function() {
24  // The tests in this suite are particularly slow, as they perform a lot of actions
25  this.timeout(10000);
26
27  [false, true].forEach(sourceMaps => {
28    const withOrWithout = sourceMaps ? 'with source maps' : 'without source maps';
29    const targetPage = sourceMaps ? 'sources/multi-workers-sourcemap.html' : 'sources/multi-workers.html';
30    const scriptFile = sourceMaps ? 'multi-workers.min.js' : 'multi-workers.js';
31    function workerFileSelectors(workerIndex: number) {
32      return createSelectorsForWorkerFile(scriptFile, 'test/e2e/resources/sources', 'multi-workers.js', workerIndex);
33    }
34
35    async function validateNavigationTree() {
36      await step('Ensure 10 workers exist', async () => {
37        await waitFor(workerFileSelectors(10).rootSelector);
38      });
39    }
40
41    async function validateBreakpoints(frontend: puppeteer.Page) {
42      assert.deepEqual(await getBreakpointDecorators(frontend), [6, 12]);
43      assert.deepEqual(await getBreakpointDecorators(frontend, true), [6]);
44    }
45
46    it(`loads scripts exactly once on reload ${withOrWithout}`, async () => {
47      // Have the target load the page.
48      await goToResource(targetPage);
49
50      await click('#tab-sources');
51      await validateNavigationTree();
52      await openNestedWorkerFile(workerFileSelectors(1));
53
54      // Look at source tabs
55      await validateSourceTabs();
56
57      // Reload page
58      await goToResource(targetPage);
59
60      // Check workers again
61      await validateNavigationTree();
62
63      // Look at source tabs again
64      await validateSourceTabs();
65    });
66
67    // Flaky test
68    it.skip(`[crbug.com/1073406]: loads scripts exactly once on break ${withOrWithout}`, async () => {
69      const {target} = getBrowserAndPages();
70
71      // Have the target load the page.
72      await goToResource(targetPage);
73
74      await click('#tab-sources');
75
76      await validateNavigationTree();
77
78      // Send message to a worker to trigger break
79      await target.evaluate('workers[3].postMessage({command:"break"});');
80
81      // Should automatically switch to sources tab.
82
83      // Validate that we are paused by locating the resume button
84      await waitFor(RESUME_BUTTON);
85
86      // Look at source tabs
87      await validateSourceTabs();
88
89      // Continue
90      await click(RESUME_BUTTON);
91      // Verify that we have resumed.
92      await waitFor(PAUSE_BUTTON);
93
94      await target.evaluate('workers[7].postMessage({command:"break"});');
95
96      // Validate that we are paused
97      await waitFor(RESUME_BUTTON);
98
99      // Look at source tabs
100      await validateSourceTabs();
101    });
102
103    it(`shows exactly one breakpoint ${withOrWithout}`, async () => {
104      const {frontend} = getBrowserAndPages();
105      // Have the target load the page.
106      await goToResource(targetPage);
107
108      await click('#tab-sources');
109      // Wait for all workers to load
110      await validateNavigationTree();
111      // Open file from second worker
112      await openNestedWorkerFile(workerFileSelectors(2));
113      // Set a breakpoint
114      await addBreakpointForLine(frontend, 6);
115
116      await waitFor('.breakpoint-entry');
117      const breakpoints = (await $$('.breakpoint-entry')).length;
118      assert.strictEqual(breakpoints, 1);
119    });
120
121    describe(`copies breakpoints between workers ${withOrWithout}`, () => {
122      beforeEach(async () => {
123        const {frontend} = getBrowserAndPages();
124        // Have the target load the page.
125        await goToResource(targetPage);
126
127        await click('#tab-sources');
128        // Wait for all workers to load
129        await validateNavigationTree();
130
131        await step('Open file from second worker', async () => {
132          await openNestedWorkerFile(workerFileSelectors(2));
133        });
134
135        await step('Set two breakpoints', async () => {
136          await addBreakpointForLine(frontend, 6);
137        });
138
139        await step('Disable first breakpoint', async () => {
140          const bpEntry = await waitFor('.breakpoint-entry');
141          const bpCheckbox = await waitFor('input', bpEntry);
142          await bpCheckbox.evaluate(n => (n as HTMLElement).click());
143          await frontend.waitForSelector('.cm-breakpoint-disabled');
144        });
145
146        await step('Add another breakpoint', async () => {
147          await addBreakpointForLine(frontend, 12);
148        });
149
150        await step('Check breakpoints', async () => {
151          await validateBreakpoints(frontend);
152        });
153
154        await step('Close tab', async () => {
155          await click('[aria-label="Close multi-workers.js"]');
156        });
157      });
158
159      it('when opening different file in editor', async () => {
160        const {frontend} = getBrowserAndPages();
161
162        // Open different worker
163        await step('Open different worker', async () => {
164          await openNestedWorkerFile(workerFileSelectors(3));
165        });
166
167        await step('Check breakpoints', async () => {
168          await validateBreakpoints(frontend);
169        });
170      });
171
172      it('after reloading', async () => {
173        const {target, frontend} = getBrowserAndPages();
174
175        await step('Reload page', async () => {
176          await target.reload();
177        });
178
179        // FIXME(crbug/1112692): Refactor test to remove the timeout.
180        await timeout(100);
181
182        await step('Open different worker', async () => {
183          await openNestedWorkerFile(workerFileSelectors(4));
184        });
185
186        await step('Check breakpoints', async () => {
187          await validateBreakpoints(frontend);
188        });
189      });
190    });
191
192    describe(`hits breakpoints added to workers ${withOrWithout}`, () => {
193      beforeEach(async () => {
194        const {frontend} = getBrowserAndPages();
195
196        // Have the target load the page.
197        await goToResource(targetPage);
198
199        await step('Open sources panel', async () => {
200          await click('#tab-sources');
201        });
202
203        await validateNavigationTree();
204
205        await step('Open second worker file', async () => {
206          await openNestedWorkerFile(workerFileSelectors(2));
207        });
208
209        await step('Set breakpoint', async () => {
210          await addBreakpointForLine(frontend, 6);
211        });
212      });
213
214      // Flaky test
215      it.skip('[crbug.com/1073406]: for pre-loaded workers', async () => {
216        const {target} = getBrowserAndPages();
217        // Send message to a worker to trigger break
218        await target.evaluate('workers[5].postMessage({});');
219
220        // Validate that we are paused by locating the resume button
221        await waitFor(RESUME_BUTTON);
222
223        // Validate that the source line is highlighted
224        assert.strictEqual(await getExecutionLine(), 6);
225
226        // Look at source tabs
227        await validateSourceTabs();
228      });
229
230      // Flaky test
231      it.skip('[crbug.com/1073406] for newly created workers', async () => {
232        const {target} = getBrowserAndPages();
233        await step('Launch new worker to hit breakpoint', async () => {
234          await target.evaluate(`new Worker('${scriptFile}').postMessage({});`);
235        });
236
237        await step('Validate that we are paused', async () => {
238          await waitFor(RESUME_BUTTON);
239        });
240
241        await step('Validate source line is highlighted', async () => {
242          assert.strictEqual(await getExecutionLine(), 6);
243        });
244
245        await validateSourceTabs();
246      });
247    });
248  });
249});
250