mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-09-28 21:24:26 +08:00
Bug: 361078921 Change-Id: I82dcc76d498156aeb7a95f40372477c024e8d17d Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6258000 Commit-Queue: Alex Rudenko <alexrudenko@chromium.org> Reviewed-by: Nikolay Vitkov <nvitkov@chromium.org> Commit-Queue: Nikolay Vitkov <nvitkov@chromium.org> Auto-Submit: Alex Rudenko <alexrudenko@chromium.org>
301 lines
12 KiB
TypeScript
301 lines
12 KiB
TypeScript
// Copyright 2021 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import {assert} from 'chai';
|
|
|
|
import {
|
|
click,
|
|
clickElement,
|
|
drainFrontendTaskQueue,
|
|
enableExperiment,
|
|
getBrowserAndPages,
|
|
goToResource,
|
|
hover,
|
|
step,
|
|
waitFor,
|
|
waitForFunction,
|
|
withControlOrMetaKey,
|
|
} from '../../shared/helper.js';
|
|
import {reloadDevTools} from '../helpers/cross-tool-helper.js';
|
|
import {getMenuItemAtPosition, getMenuItemTitleAtPosition, openFileQuickOpen} from '../helpers/quick_open-helpers.js';
|
|
import {
|
|
addBreakpointForLine,
|
|
CODE_LINE_COLUMN_SELECTOR,
|
|
getBreakpointHitLocation,
|
|
isBreakpointSet,
|
|
isEqualOrAbbreviation,
|
|
openSourceCodeEditorForFile,
|
|
PAUSE_INDICATOR_SELECTOR,
|
|
RESUME_BUTTON,
|
|
retrieveTopCallFrameWithoutResuming,
|
|
} from '../helpers/sources-helpers.js';
|
|
|
|
async function waitForTopCallFrameChanged(previousCallFrame: string, updatedCallFrame: string) {
|
|
await waitForFunction(async () => {
|
|
const actualTopCallFrame = await retrieveTopCallFrameWithoutResuming();
|
|
assert.isTrue(actualTopCallFrame === previousCallFrame || actualTopCallFrame === updatedCallFrame);
|
|
return actualTopCallFrame === updatedCallFrame;
|
|
});
|
|
}
|
|
|
|
async function assertScriptLocation(expectedLocation: string) {
|
|
const scriptLocation = await retrieveTopCallFrameWithoutResuming();
|
|
if (!scriptLocation) {
|
|
assert.fail('Unable to retrieve script location for call frame');
|
|
}
|
|
assert.isTrue(isEqualOrAbbreviation(scriptLocation, expectedLocation));
|
|
}
|
|
|
|
describe('The Sources Tab', function() {
|
|
const CLICK_BREAKPOINT_SCRIPT = 'click-breakpoint.js';
|
|
const CLICK_BREAKPOINT_HTML = 'click-breakpoint.html';
|
|
|
|
// Some of these tests that use instrumentation breakpoints
|
|
// can be slower on mac and windows. Increaese the timeout for them.
|
|
if (this.timeout() !== 0) {
|
|
this.timeout(10000);
|
|
}
|
|
|
|
it('sets and hits breakpoints in JavaScript', async () => {
|
|
const {target, frontend} = getBrowserAndPages();
|
|
await openSourceCodeEditorForFile(CLICK_BREAKPOINT_SCRIPT, CLICK_BREAKPOINT_HTML);
|
|
await addBreakpointForLine(frontend, 4);
|
|
|
|
const scriptEvaluation = target.evaluate('f2();');
|
|
|
|
const scriptLocation = await retrieveTopCallFrameWithoutResuming();
|
|
assert.deepEqual(scriptLocation, `${CLICK_BREAKPOINT_SCRIPT}:4`);
|
|
|
|
const breakpointLocation = await getBreakpointHitLocation();
|
|
assert.deepEqual(breakpointLocation, scriptLocation);
|
|
|
|
await click(RESUME_BUTTON);
|
|
await scriptEvaluation;
|
|
});
|
|
|
|
it('can disable and re-enable breakpoints in JavaScript', async () => {
|
|
const {target, frontend} = getBrowserAndPages();
|
|
await openSourceCodeEditorForFile(CLICK_BREAKPOINT_SCRIPT, CLICK_BREAKPOINT_HTML);
|
|
|
|
// After adding a breakpoint, we expect the script to pause. Resume afterwards.
|
|
await addBreakpointForLine(frontend, 4);
|
|
await testScriptPauseAndResume();
|
|
|
|
// Disable breakpoint. This time, we should not pause but be able to
|
|
// run the script until the end.
|
|
await click(`[aria-label="${CLICK_BREAKPOINT_SCRIPT}"] [aria-label="checked"] input`);
|
|
await target.evaluate('f2();');
|
|
|
|
// Re-enable breakpoint. Again, we should expect a pause and resume to finish the script.
|
|
await click(`[aria-label="${CLICK_BREAKPOINT_SCRIPT}"] [aria-label="unchecked"] input`);
|
|
await testScriptPauseAndResume();
|
|
|
|
async function testScriptPauseAndResume() {
|
|
const scriptEvaluation = target.evaluate('f2();');
|
|
|
|
const scriptLocation = await retrieveTopCallFrameWithoutResuming();
|
|
assert.deepEqual(scriptLocation, `${CLICK_BREAKPOINT_SCRIPT}:4`);
|
|
|
|
await click(RESUME_BUTTON);
|
|
await scriptEvaluation;
|
|
}
|
|
});
|
|
|
|
it('can set and remove breakpoints in JavaScript', async () => {
|
|
const {target, frontend} = getBrowserAndPages();
|
|
await openSourceCodeEditorForFile(CLICK_BREAKPOINT_SCRIPT, CLICK_BREAKPOINT_HTML);
|
|
await addBreakpointForLine(frontend, 4);
|
|
|
|
// Hover over breakpoint.
|
|
await hover(`[aria-label="${CLICK_BREAKPOINT_SCRIPT}"] [aria-label="checked"]`);
|
|
|
|
// Remove breakpoint.
|
|
await click(`[aria-label="${CLICK_BREAKPOINT_SCRIPT}"] [aria-label="Remove breakpoint"]`);
|
|
|
|
// Running the function should not pause anywhere.
|
|
await target.evaluate('f2();');
|
|
});
|
|
|
|
it('doesn\'t synchronize breakpoints between scripts and source-mapped scripts', async () => {
|
|
const {frontend} = getBrowserAndPages();
|
|
|
|
// Navigate to page with sourceURL annotation and set breakpoint in line 2.
|
|
await openSourceCodeEditorForFile('breakpoint-conflict.js', 'breakpoint-conflict-source-url.html');
|
|
await addBreakpointForLine(frontend, 2);
|
|
|
|
// Navigate to page with sourceMappingURL annotation and check that breakpoint did not sync.
|
|
await openSourceCodeEditorForFile('breakpoint-conflict.js', 'breakpoint-conflict-source-map.html');
|
|
assert.isFalse(await isBreakpointSet(2), 'Breakpoint found on line 2 which shouldn\'t be there');
|
|
});
|
|
|
|
it('stops at each breakpoint on resume (using F8) on target', async () => {
|
|
const {target, frontend} = getBrowserAndPages();
|
|
await step('navigate to page', async () => {
|
|
await openSourceCodeEditorForFile(CLICK_BREAKPOINT_SCRIPT, CLICK_BREAKPOINT_HTML);
|
|
});
|
|
|
|
await step('add a breakpoint to line No.3, 4, and 9', async () => {
|
|
await addBreakpointForLine(frontend, 3);
|
|
await addBreakpointForLine(frontend, 4);
|
|
await addBreakpointForLine(frontend, 9);
|
|
});
|
|
|
|
let scriptEvaluation: Promise<void>;
|
|
await step('trigger evaluation of script', async () => {
|
|
scriptEvaluation = target.evaluate('f2();') as Promise<void>;
|
|
});
|
|
|
|
await step('wait for pause and check if we stopped at line 3', async () => {
|
|
await waitFor(PAUSE_INDICATOR_SELECTOR);
|
|
const scriptLocation = await retrieveTopCallFrameWithoutResuming();
|
|
assert.deepEqual(scriptLocation, `${CLICK_BREAKPOINT_SCRIPT}:3`);
|
|
});
|
|
|
|
await step('resume and wait until we have hit the next breakpoint (3->4)', async () => {
|
|
await target.keyboard.press('F8');
|
|
await waitForTopCallFrameChanged(`${CLICK_BREAKPOINT_SCRIPT}:3`, `${CLICK_BREAKPOINT_SCRIPT}:4`);
|
|
});
|
|
|
|
await step('resume and wait until we have hit the next breakpoint (4->9)', async () => {
|
|
await target.keyboard.press('F8');
|
|
await waitForTopCallFrameChanged(`${CLICK_BREAKPOINT_SCRIPT}:4`, `${CLICK_BREAKPOINT_SCRIPT}:9`);
|
|
});
|
|
|
|
await step('resume and wait until script finishes execution', async () => {
|
|
await frontend.keyboard.press('F8');
|
|
await scriptEvaluation;
|
|
});
|
|
});
|
|
|
|
it('can hit a breakpoint on the main thread on a fresh DevTools', async () => {
|
|
await enableExperiment('instrumentation-breakpoints');
|
|
const {frontend} = getBrowserAndPages();
|
|
|
|
await step('navigate to a page and open the Sources tab', async () => {
|
|
await openSourceCodeEditorForFile('breakpoint-hit-on-first-load.js', 'breakpoint-hit-on-first-load.html');
|
|
});
|
|
|
|
await step('add a breakpoint to the beginning of the script', async () => {
|
|
await addBreakpointForLine(frontend, 1);
|
|
});
|
|
|
|
await step('Navigate to a different site to refresh devtools and remove back-end state', async () => {
|
|
await reloadDevTools({removeBackendState: true, selectedPanel: {name: 'sources'}});
|
|
});
|
|
|
|
await step('Navigate back to test page', () => {
|
|
void goToResource('sources/breakpoint-hit-on-first-load.html');
|
|
});
|
|
|
|
await step('wait for pause and check if we stopped at line 1', async () => {
|
|
await waitFor(PAUSE_INDICATOR_SELECTOR);
|
|
await assertScriptLocation('breakpoint-hit-on-first-load.js:1');
|
|
});
|
|
|
|
await step('Resume', async () => {
|
|
await click(RESUME_BUTTON);
|
|
});
|
|
});
|
|
|
|
// Skip test for now to land unrelated changes that will fail this test. Inline scripts without source
|
|
// urls are currently not correctly handled.
|
|
it.skip(
|
|
'[crbug.com/1229541] can hit a breakpoint in an inline script on the main thread on a fresh DevTools',
|
|
async () => {
|
|
await enableExperiment('instrumentation-breakpoints');
|
|
const {frontend} = getBrowserAndPages();
|
|
|
|
await step('navigate to a page and open the Sources tab', async () => {
|
|
await openSourceCodeEditorForFile('breakpoint-hit-on-first-load.html', 'breakpoint-hit-on-first-load.html');
|
|
});
|
|
|
|
await step('add a breakpoint to the beginning of the inline script', async () => {
|
|
await addBreakpointForLine(frontend, 9);
|
|
});
|
|
|
|
await step('Navigate to a different site to refresh devtools and remove back-end state', async () => {
|
|
await reloadDevTools({removeBackendState: true, selectedPanel: {name: 'sources'}});
|
|
});
|
|
|
|
await step('Navigate back to test page', () => {
|
|
void goToResource('sources/breakpoint-hit-on-first-load.html');
|
|
});
|
|
|
|
await step('wait for pause and check if we stopped at line 9', async () => {
|
|
await waitFor(PAUSE_INDICATOR_SELECTOR);
|
|
await assertScriptLocation('breakpoint-hit-on-first-load.html:9');
|
|
});
|
|
|
|
await step('Resume', async () => {
|
|
await click(RESUME_BUTTON);
|
|
});
|
|
});
|
|
|
|
it('can hit a breakpoint in an inline script with sourceURL comment on the main thread on a fresh DevTools',
|
|
async () => {
|
|
await enableExperiment('instrumentation-breakpoints');
|
|
const {frontend} = getBrowserAndPages();
|
|
|
|
await step('navigate to a page and open the Sources tab', async () => {
|
|
await openSourceCodeEditorForFile('breakpoint-hit-on-first-load.html', 'breakpoint-hit-on-first-load.html');
|
|
});
|
|
|
|
await step('open the hello.js file (inline script)', async () => {
|
|
await openFileQuickOpen();
|
|
await frontend.keyboard.type('hello.js');
|
|
// TODO: it should actually wait for rendering to finish.
|
|
await drainFrontendTaskQueue();
|
|
|
|
const firstItemTitle = await getMenuItemTitleAtPosition(0);
|
|
const firstItem = await getMenuItemAtPosition(0);
|
|
assert.strictEqual(firstItemTitle, 'hello.js');
|
|
await clickElement(firstItem);
|
|
});
|
|
|
|
await step('add a breakpoint to the beginning of the inline script with sourceURL', async () => {
|
|
await addBreakpointForLine(frontend, 2);
|
|
});
|
|
|
|
await step('Navigate to a different site to refresh devtools and remove back-end state', async () => {
|
|
await reloadDevTools({removeBackendState: true, selectedPanel: {name: 'sources'}});
|
|
});
|
|
|
|
await step('Navigate back to test page', () => {
|
|
void goToResource('sources/breakpoint-hit-on-first-load.html');
|
|
});
|
|
|
|
await step('wait for pause and check if we stopped at line 2', async () => {
|
|
await waitFor(PAUSE_INDICATOR_SELECTOR);
|
|
await assertScriptLocation('hello.js:2');
|
|
});
|
|
|
|
await step('Resume', async () => {
|
|
await click(RESUME_BUTTON);
|
|
});
|
|
});
|
|
|
|
describe('The breakpoint edit dialog', () => {
|
|
it('shows up on Ctrl/Meta + click if no breakpoint was set', async () => {
|
|
await openSourceCodeEditorForFile(CLICK_BREAKPOINT_SCRIPT, CLICK_BREAKPOINT_HTML);
|
|
const lineNumberColumn = await waitFor(CODE_LINE_COLUMN_SELECTOR);
|
|
await withControlOrMetaKey(async () => {
|
|
await click('text/4', {root: lineNumberColumn});
|
|
});
|
|
await waitFor('.sources-edit-breakpoint-dialog');
|
|
});
|
|
|
|
it('shows up on Ctrl/Meta + click if breakpoint was already set', async () => {
|
|
const {frontend} = getBrowserAndPages();
|
|
await openSourceCodeEditorForFile(CLICK_BREAKPOINT_SCRIPT, CLICK_BREAKPOINT_HTML);
|
|
await addBreakpointForLine(frontend, 4);
|
|
|
|
const lineNumberColumn = await waitFor(CODE_LINE_COLUMN_SELECTOR);
|
|
await withControlOrMetaKey(async () => {
|
|
await click('text/4', {root: lineNumberColumn});
|
|
});
|
|
await waitFor('.sources-edit-breakpoint-dialog');
|
|
});
|
|
});
|
|
});
|