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