summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/actions/tests/pending-breakpoints.spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/actions/tests/pending-breakpoints.spec.js')
-rw-r--r--devtools/client/debugger/src/actions/tests/pending-breakpoints.spec.js439
1 files changed, 439 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/actions/tests/pending-breakpoints.spec.js b/devtools/client/debugger/src/actions/tests/pending-breakpoints.spec.js
new file mode 100644
index 0000000000..f43686d199
--- /dev/null
+++ b/devtools/client/debugger/src/actions/tests/pending-breakpoints.spec.js
@@ -0,0 +1,439 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+// @flow
+
+// TODO: we would like to mock this in the local tests
+import {
+ generateBreakpoint,
+ mockPendingBreakpoint,
+} from "./helpers/breakpoints.js";
+
+import { mockCommandClient } from "./helpers/mockCommandClient";
+import { asyncStore } from "../../utils/prefs";
+
+function loadInitialState(opts = {}) {
+ const mockedPendingBreakpoint = mockPendingBreakpoint({ ...opts, column: 2 });
+ const id = makePendingLocationId(mockedPendingBreakpoint.location);
+ asyncStore.pendingBreakpoints = { [id]: mockedPendingBreakpoint };
+
+ return { pendingBreakpoints: asyncStore.pendingBreakpoints };
+}
+
+jest.mock("../../utils/prefs", () => ({
+ prefs: {
+ clientSourceMapsEnabled: true,
+ expressions: [],
+ },
+ asyncStore: {
+ pendingBreakpoints: {},
+ },
+ clear: jest.fn(),
+ features: {
+ inlinePreview: true,
+ },
+}));
+
+import {
+ createStore,
+ selectors,
+ actions,
+ makeSource,
+ makeSourceURL,
+ waitForState,
+} from "../../utils/test-head";
+
+import sourceMaps from "devtools-source-map";
+
+import { makePendingLocationId } from "../../utils/breakpoint";
+function mockClient(bpPos = {}) {
+ return {
+ ...mockCommandClient,
+ setSkipPausing: jest.fn(),
+ getSourceActorBreakpointPositions: async () => bpPos,
+ getSourceActorBreakableLines: async () => [],
+ };
+}
+
+function mockSourceMaps() {
+ return {
+ ...sourceMaps,
+ getOriginalSourceText: async id => ({
+ id,
+ text: "",
+ contentType: "text/javascript",
+ }),
+ getGeneratedRangesForOriginal: async () => [
+ { start: { line: 0, column: 0 }, end: { line: 10, column: 10 } },
+ ],
+ getOriginalLocations: async items => items,
+ };
+}
+
+describe("when adding breakpoints", () => {
+ it("a corresponding pending breakpoint should be added", async () => {
+ const { dispatch, getState, cx } = createStore(
+ mockClient({ "5": [1] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+
+ const source = await dispatch(
+ actions.newGeneratedSource(makeSource("foo.js"))
+ );
+ await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
+ await dispatch(actions.loadSourceText({ cx, source }));
+
+ const bp = generateBreakpoint("foo.js", 5, 1);
+ const id = makePendingLocationId(bp.location);
+
+ await dispatch(actions.addBreakpoint(cx, bp.location));
+ const pendingBps = selectors.getPendingBreakpoints(getState());
+
+ expect(selectors.getPendingBreakpointList(getState())).toHaveLength(2);
+ expect(pendingBps[id]).toMatchSnapshot();
+ });
+
+ describe("adding and deleting breakpoints", () => {
+ let breakpoint1;
+ let breakpoint2;
+ let breakpointLocationId1;
+ let breakpointLocationId2;
+
+ beforeEach(() => {
+ breakpoint1 = generateBreakpoint("foo");
+ breakpoint2 = generateBreakpoint("foo2");
+ breakpointLocationId1 = makePendingLocationId(breakpoint1.location);
+ breakpointLocationId2 = makePendingLocationId(breakpoint2.location);
+ });
+
+ it("add a corresponding pendingBreakpoint for each addition", async () => {
+ const { dispatch, getState, cx } = createStore(
+ mockClient({ "5": [0] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+
+ await dispatch(actions.newGeneratedSource(makeSource("foo")));
+ await dispatch(actions.newGeneratedSource(makeSource("foo2")));
+
+ const source1 = await dispatch(
+ actions.newGeneratedSource(makeSource("foo"))
+ );
+ const source2 = await dispatch(
+ actions.newGeneratedSource(makeSource("foo2"))
+ );
+
+ await dispatch(actions.loadSourceText({ cx, source: source1 }));
+ await dispatch(actions.loadSourceText({ cx, source: source2 }));
+
+ await dispatch(actions.addBreakpoint(cx, breakpoint1.location));
+ await dispatch(actions.addBreakpoint(cx, breakpoint2.location));
+
+ const pendingBps = selectors.getPendingBreakpoints(getState());
+
+ // NOTE the sourceId should be `foo2/originalSource`, but is `foo2`
+ // because we do not have a real source map for `getOriginalLocation`
+ // to map.
+ expect(pendingBps[breakpointLocationId1]).toMatchSnapshot();
+ expect(pendingBps[breakpointLocationId2]).toMatchSnapshot();
+ });
+
+ it("hidden breakponts do not create pending bps", async () => {
+ const { dispatch, getState, cx } = createStore(
+ mockClient({ "5": [0] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+
+ await dispatch(actions.newGeneratedSource(makeSource("foo")));
+ const source = await dispatch(
+ actions.newGeneratedSource(makeSource("foo"))
+ );
+ await dispatch(actions.loadSourceText({ cx, source }));
+
+ await dispatch(
+ actions.addBreakpoint(cx, breakpoint1.location, { hidden: true })
+ );
+ const pendingBps = selectors.getPendingBreakpoints(getState());
+
+ expect(pendingBps[breakpointLocationId1]).toBeUndefined();
+ });
+
+ it("remove a corresponding pending breakpoint when deleting", async () => {
+ const { dispatch, getState, cx } = createStore(
+ mockClient({ "5": [0] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+
+ await dispatch(actions.newGeneratedSource(makeSource("foo")));
+ await dispatch(actions.newGeneratedSource(makeSource("foo2")));
+
+ const source1 = await dispatch(
+ actions.newGeneratedSource(makeSource("foo"))
+ );
+ const source2 = await dispatch(
+ actions.newGeneratedSource(makeSource("foo2"))
+ );
+
+ await dispatch(actions.loadSourceText({ cx, source: source1 }));
+ await dispatch(actions.loadSourceText({ cx, source: source2 }));
+
+ await dispatch(actions.addBreakpoint(cx, breakpoint1.location));
+ await dispatch(actions.addBreakpoint(cx, breakpoint2.location));
+ await dispatch(actions.removeBreakpoint(cx, breakpoint1));
+
+ const pendingBps = selectors.getPendingBreakpoints(getState());
+ expect(pendingBps.hasOwnProperty(breakpointLocationId1)).toBe(false);
+ expect(pendingBps.hasOwnProperty(breakpointLocationId2)).toBe(true);
+ });
+ });
+});
+
+describe("when changing an existing breakpoint", () => {
+ it("updates corresponding pendingBreakpoint", async () => {
+ const { dispatch, getState, cx } = createStore(
+ mockClient({ "5": [0] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+ const bp = generateBreakpoint("foo");
+ const id = makePendingLocationId(bp.location);
+
+ const source = await dispatch(
+ actions.newGeneratedSource(makeSource("foo"))
+ );
+ await dispatch(actions.newGeneratedSource(makeSource("foo")));
+ await dispatch(actions.loadSourceText({ cx, source }));
+
+ await dispatch(actions.addBreakpoint(cx, bp.location));
+ await dispatch(
+ actions.setBreakpointOptions(cx, bp.location, { condition: "2" })
+ );
+ const bps = selectors.getPendingBreakpoints(getState());
+ const breakpoint = bps[id];
+ expect(breakpoint.options.condition).toBe("2");
+ });
+
+ it("if disabled, updates corresponding pendingBreakpoint", async () => {
+ const { dispatch, getState, cx } = createStore(
+ mockClient({ "5": [0] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+ const bp = generateBreakpoint("foo");
+ const id = makePendingLocationId(bp.location);
+
+ await dispatch(actions.newGeneratedSource(makeSource("foo")));
+
+ const source = await dispatch(
+ actions.newGeneratedSource(makeSource("foo"))
+ );
+ await dispatch(actions.loadSourceText({ cx, source }));
+
+ await dispatch(actions.addBreakpoint(cx, bp.location));
+ await dispatch(actions.disableBreakpoint(cx, bp));
+ const bps = selectors.getPendingBreakpoints(getState());
+ const breakpoint = bps[id];
+ expect(breakpoint.disabled).toBe(true);
+ });
+
+ it("does not delete the pre-existing pendingBreakpoint", async () => {
+ const { dispatch, getState, cx } = createStore(
+ mockClient({ "5": [0] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+ const bp = generateBreakpoint("foo.js");
+
+ const source = await dispatch(
+ actions.newGeneratedSource(makeSource("foo.js"))
+ );
+ await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
+ await dispatch(actions.loadSourceText({ cx, source }));
+
+ const id = makePendingLocationId(bp.location);
+
+ await dispatch(actions.addBreakpoint(cx, bp.location));
+ await dispatch(
+ actions.setBreakpointOptions(cx, bp.location, { condition: "2" })
+ );
+ const bps = selectors.getPendingBreakpoints(getState());
+ const breakpoint = bps[id];
+ expect(breakpoint.options.condition).toBe("2");
+ });
+});
+
+describe("initializing when pending breakpoints exist in prefs", () => {
+ it("syncs pending breakpoints", async () => {
+ const { getState } = createStore(
+ mockClient({ "5": [0] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+ const bps = selectors.getPendingBreakpoints(getState());
+ expect(bps).toMatchSnapshot();
+ });
+
+ it("re-adding breakpoints update existing pending breakpoints", async () => {
+ const { dispatch, getState, cx } = createStore(
+ mockClient({ "5": [1, 2] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+ const bar = generateBreakpoint("bar.js", 5, 1);
+
+ await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
+
+ const source = await dispatch(
+ actions.newGeneratedSource(makeSource("bar.js"))
+ );
+ await dispatch(actions.loadSourceText({ cx, source }));
+ await dispatch(actions.addBreakpoint(cx, bar.location));
+
+ const bps = selectors.getPendingBreakpointList(getState());
+ expect(bps).toHaveLength(2);
+ });
+
+ it("adding bps doesn't remove existing pending breakpoints", async () => {
+ const { dispatch, getState, cx } = createStore(
+ mockClient({ "5": [0] }),
+ loadInitialState(),
+ mockSourceMaps()
+ );
+ const bp = generateBreakpoint("foo.js");
+
+ const source = await dispatch(
+ actions.newGeneratedSource(makeSource("foo.js"))
+ );
+ await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
+ await dispatch(actions.loadSourceText({ cx, source }));
+
+ await dispatch(actions.addBreakpoint(cx, bp.location));
+
+ const bps = selectors.getPendingBreakpointList(getState());
+ expect(bps).toHaveLength(2);
+ });
+});
+
+describe("initializing with disabled pending breakpoints in prefs", () => {
+ it("syncs breakpoints with pending breakpoints", async () => {
+ const store = createStore(
+ mockClient({ "5": [2] }),
+ loadInitialState({ disabled: true }),
+ mockSourceMaps()
+ );
+
+ const { getState, dispatch, cx } = store;
+
+ await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
+ const source = await dispatch(
+ actions.newGeneratedSource(makeSource("bar.js"))
+ );
+ await dispatch(actions.loadSourceText({ cx, source }));
+
+ await waitForState(store, state => {
+ const bps = selectors.getBreakpointsForSource(state, source.id);
+ return bps && Object.values(bps).length > 0;
+ });
+
+ const bp = selectors.getBreakpointForLocation(getState(), {
+ line: 5,
+ column: 2,
+ sourceUrl: source.url,
+ sourceId: source.id,
+ });
+ if (!bp) {
+ throw new Error("no bp");
+ }
+ expect(bp.location.sourceId).toEqual(source.id);
+ expect(bp.disabled).toEqual(true);
+ });
+});
+
+describe("adding sources", () => {
+ it("corresponding breakpoints are added for a single source", async () => {
+ const store = createStore(
+ mockClient({ "5": [2] }),
+ loadInitialState({ disabled: true }),
+ mockSourceMaps()
+ );
+ const { getState, dispatch, cx } = store;
+
+ expect(selectors.getBreakpointCount(getState())).toEqual(0);
+
+ await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
+ const source = await dispatch(
+ actions.newGeneratedSource(makeSource("bar.js"))
+ );
+ await dispatch(actions.loadSourceText({ cx, source }));
+
+ await waitForState(store, state => selectors.getBreakpointCount(state) > 0);
+
+ expect(selectors.getBreakpointCount(getState())).toEqual(1);
+ });
+
+ it("corresponding breakpoints are added to the original source", async () => {
+ const sourceURL = makeSourceURL("bar.js");
+ const store = createStore(mockClient({ "5": [2] }), loadInitialState(), {
+ getOriginalURLs: async source => [
+ {
+ id: sourceMaps.generatedToOriginalId(source.id, sourceURL),
+ url: sourceURL,
+ },
+ ],
+ getOriginalSourceText: async () => ({ text: "" }),
+ getGeneratedLocation: async location => ({
+ line: location.line,
+ column: location.column,
+ sourceId: location.sourceId,
+ }),
+ getOriginalLocation: async location => location,
+ getGeneratedRangesForOriginal: async () => [
+ { start: { line: 0, column: 0 }, end: { line: 10, column: 10 } },
+ ],
+ getOriginalLocations: async items =>
+ items.map(item => ({
+ ...item,
+ sourceId: sourceMaps.generatedToOriginalId(item.sourceId, sourceURL),
+ })),
+ });
+
+ const { getState, dispatch } = store;
+
+ expect(selectors.getBreakpointCount(getState())).toEqual(0);
+
+ await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
+ await dispatch(
+ actions.newGeneratedSource(makeSource("bar.js", { sourceMapURL: "foo" }))
+ );
+
+ await waitForState(store, state => selectors.getBreakpointCount(state) > 0);
+
+ expect(selectors.getBreakpointCount(getState())).toEqual(1);
+ });
+
+ it("add corresponding breakpoints for multiple sources", async () => {
+ const store = createStore(
+ mockClient({ "5": [2] }),
+ loadInitialState({ disabled: true }),
+ mockSourceMaps()
+ );
+ const { getState, dispatch, cx } = store;
+
+ expect(selectors.getBreakpointCount(getState())).toEqual(0);
+
+ await dispatch(actions.newGeneratedSource(makeSource("bar.js")));
+ await dispatch(actions.newGeneratedSource(makeSource("foo.js")));
+ const [source1, source2] = await dispatch(
+ actions.newGeneratedSources([makeSource("bar.js"), makeSource("foo.js")])
+ );
+ await dispatch(actions.loadSourceText({ cx, source: source1 }));
+ await dispatch(actions.loadSourceText({ cx, source: source2 }));
+
+ await waitForState(store, state => selectors.getBreakpointCount(state) > 0);
+ expect(selectors.getBreakpointCount(getState())).toEqual(1);
+ });
+});