diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /devtools/client/debugger/src/utils/pause/frames | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/debugger/src/utils/pause/frames')
12 files changed, 819 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/utils/pause/frames/annotateFrames.js b/devtools/client/debugger/src/utils/pause/frames/annotateFrames.js new file mode 100644 index 0000000000..bd4114c5ab --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/annotateFrames.js @@ -0,0 +1,80 @@ +/* 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 + +import { flatMap, zip, range } from "lodash"; + +import type { Frame } from "../../../types"; +import { getFrameUrl } from "./getFrameUrl"; +import { getLibraryFromUrl } from "./getLibraryFromUrl"; + +type AnnotatedFrame = + | {| + ...Frame, + library: string, + |} + | Frame; + +export function annotateFrames(frames: Frame[]): AnnotatedFrame[] { + const annotatedFrames = frames.map(f => annotateFrame(f, frames)); + return annotateBabelAsyncFrames(annotatedFrames); +} + +function annotateFrame(frame: Frame, frames: Frame[]): AnnotatedFrame { + const library = getLibraryFromUrl(frame, frames); + if (library) { + return { ...frame, library }; + } + + return frame; +} + +function annotateBabelAsyncFrames(frames: Frame[]): Frame[] { + const babelFrameIndexes = getBabelFrameIndexes(frames); + const isBabelFrame = frameIndex => babelFrameIndexes.includes(frameIndex); + + return frames.map((frame, frameIndex) => + isBabelFrame(frameIndex) ? { ...frame, library: "Babel" } : frame + ); +} + +// Receives an array of frames and looks for babel async +// call stack groups. +function getBabelFrameIndexes(frames) { + const startIndexes = frames.reduce((accumulator, frame, index) => { + if ( + getFrameUrl(frame).match(/regenerator-runtime/i) && + frame.displayName === "tryCatch" + ) { + return [...accumulator, index]; + } + return accumulator; + }, []); + + const endIndexes = frames.reduce((accumulator, frame, index) => { + if ( + getFrameUrl(frame).match(/_microtask/i) && + frame.displayName === "flush" + ) { + return [...accumulator, index]; + } + if (frame.displayName === "_asyncToGenerator/<") { + return [...accumulator, index + 1]; + } + return accumulator; + }, []); + + if (startIndexes.length != endIndexes.length || startIndexes.length === 0) { + return frames; + } + + // Receives an array of start and end index tuples and returns + // an array of async call stack index ranges. + // e.g. [[1,3], [5,7]] => [[1,2,3], [5,6,7]] + // $FlowIgnore + return flatMap(zip(startIndexes, endIndexes), ([startIndex, endIndex]) => + range(startIndex, endIndex + 1) + ); +} diff --git a/devtools/client/debugger/src/utils/pause/frames/collapseFrames.js b/devtools/client/debugger/src/utils/pause/frames/collapseFrames.js new file mode 100644 index 0000000000..d39a26d574 --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/collapseFrames.js @@ -0,0 +1,69 @@ +/* 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 + +import { findIndex } from "lodash"; + +// eslint-disable-next-line max-len +import type { Frame } from "../../../types"; +import { getFrameUrl } from "./getFrameUrl"; + +function collapseLastFrames(frames) { + const index = findIndex(frames, frame => + getFrameUrl(frame).match(/webpack\/bootstrap/i) + ); + + if (index == -1) { + return { newFrames: frames, lastGroup: [] }; + } + + const newFrames = frames.slice(0, index); + const lastGroup = frames.slice(index); + return { newFrames, lastGroup }; +} + +type FrameGroup = Frame[]; +type GroupedFrames = Array<FrameGroup | Frame>; + +export function collapseFrames(frames: Frame[]): GroupedFrames { + // We collapse groups of one so that user frames + // are not in a group of one + function addGroupToList(group, list) { + if (!group) { + return list; + } + + if (group.length > 1) { + list.push(group); + } else { + list = list.concat(group); + } + + return list; + } + const { newFrames, lastGroup } = collapseLastFrames(frames); + frames = newFrames; + let items = []; + let currentGroup = null; + let prevItem = null; + for (const frame of frames) { + const prevLibrary = prevItem?.library; + + if (!currentGroup) { + currentGroup = [frame]; + } else if (prevLibrary && prevLibrary == frame.library) { + currentGroup.push(frame); + } else { + items = addGroupToList(currentGroup, items); + currentGroup = [frame]; + } + + prevItem = frame; + } + + items = addGroupToList(currentGroup, items); + items = addGroupToList(lastGroup, items); + return items; +} diff --git a/devtools/client/debugger/src/utils/pause/frames/displayName.js b/devtools/client/debugger/src/utils/pause/frames/displayName.js new file mode 100644 index 0000000000..dbd53d53cd --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/displayName.js @@ -0,0 +1,108 @@ +/* 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 + +// eslint-disable-next-line max-len +import type { Frame } from "../../../types"; + +// Decodes an anonymous naming scheme that +// spider monkey implements based on "Naming Anonymous JavaScript Functions" +// http://johnjbarton.github.io/nonymous/index.html +const objectProperty = /([\w\d\$]+)$/; +const arrayProperty = /\[(.*?)\]$/; +const functionProperty = /([\w\d]+)[\/\.<]*?$/; +const annonymousProperty = /([\w\d]+)\(\^\)$/; + +export function simplifyDisplayName(displayName: string | void): string | void { + // if the display name has a space it has already been mapped + if (!displayName || /\s/.exec(displayName)) { + return displayName; + } + + const scenarios = [ + objectProperty, + arrayProperty, + functionProperty, + annonymousProperty, + ]; + + for (const reg of scenarios) { + const match = reg.exec(displayName); + if (match) { + return match[1]; + } + } + + return displayName; +} + +const displayNameMap = { + Babel: { + tryCatch: "Async", + }, + Backbone: { + "extend/child": "Create Class", + ".create": "Create Model", + }, + jQuery: { + "jQuery.event.dispatch": "Dispatch Event", + }, + React: { + // eslint-disable-next-line max-len + "ReactCompositeComponent._renderValidatedComponentWithoutOwnerOrContext/renderedElement<": + "Render", + _renderValidatedComponentWithoutOwnerOrContext: "Render", + }, + VueJS: { + "renderMixin/Vue.prototype._render": "Render", + }, + Webpack: { + // eslint-disable-next-line camelcase + __webpack_require__: "Bootstrap", + }, +}; + +function mapDisplayNames(frame, library) { + const { displayName } = frame; + return displayNameMap[library]?.[displayName] || displayName; +} + +function getFrameDisplayName(frame: Frame): string { + const { + displayName, + originalDisplayName, + userDisplayName, + name, + } = (frame: any); + return originalDisplayName || userDisplayName || displayName || name; +} + +type formatDisplayNameParams = { + shouldMapDisplayName: boolean, +}; +export function formatDisplayName( + frame: Frame, + { shouldMapDisplayName = true }: formatDisplayNameParams = {}, + l10n: typeof L10N +): string { + const { library } = frame; + let displayName = getFrameDisplayName(frame); + if (library && shouldMapDisplayName) { + displayName = mapDisplayNames(frame, library); + } + + return simplifyDisplayName(displayName) || l10n.getStr("anonymousFunction"); +} + +export function formatCopyName(frame: Frame, l10n: typeof L10N): string { + const displayName = formatDisplayName(frame, undefined, l10n); + if (!frame.source) { + throw new Error("no frame source"); + } + const fileName = frame.source.url || frame.source.id; + const frameLocation = frame.location.line; + + return `${displayName} (${fileName}#${frameLocation})`; +} diff --git a/devtools/client/debugger/src/utils/pause/frames/getFrameUrl.js b/devtools/client/debugger/src/utils/pause/frames/getFrameUrl.js new file mode 100644 index 0000000000..6a151e14f7 --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/getFrameUrl.js @@ -0,0 +1,11 @@ +/* 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 + +import type { Frame } from "../../../types"; + +export function getFrameUrl(frame: Frame) { + return frame?.source?.url ?? ""; +} diff --git a/devtools/client/debugger/src/utils/pause/frames/getLibraryFromUrl.js b/devtools/client/debugger/src/utils/pause/frames/getLibraryFromUrl.js new file mode 100644 index 0000000000..232079e3ec --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/getLibraryFromUrl.js @@ -0,0 +1,149 @@ +/* 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 + +import type { Frame } from "../../../types"; +import { getFrameUrl } from "./getFrameUrl"; + +const libraryMap = [ + { + label: "Backbone", + pattern: /backbone/i, + }, + { + label: "Babel", + pattern: /node_modules\/@babel/i, + }, + { + label: "jQuery", + pattern: /jquery/i, + }, + { + label: "Preact", + pattern: /preact/i, + }, + { + label: "React", + pattern: /(node_modules\/(?:react(-dom)?(-dev)?\/))|(react(-dom)?(-dev)?(\.[a-z]+)*\.js$)/, + }, + { + label: "Immutable", + pattern: /immutable/i, + }, + { + label: "Webpack", + pattern: /webpack\/bootstrap/i, + }, + { + label: "Express", + pattern: /node_modules\/express/, + }, + { + label: "Pug", + pattern: /node_modules\/pug/, + }, + { + label: "ExtJS", + pattern: /\/ext-all[\.\-]/, + }, + { + label: "MobX", + pattern: /mobx/i, + }, + { + label: "Underscore", + pattern: /underscore/i, + }, + { + label: "Lodash", + pattern: /lodash/i, + }, + { + label: "Ember", + pattern: /ember/i, + }, + { + label: "Choo", + pattern: /choo/i, + }, + { + label: "VueJS", + pattern: /vue(?:\.[a-z]+)*\.js/i, + }, + { + label: "RxJS", + pattern: /rxjs/i, + }, + { + label: "Angular", + pattern: /angular(?!.*\/app\/)/i, + contextPattern: /zone\.js/, + }, + { + label: "Redux", + pattern: /redux/i, + }, + { + label: "Dojo", + pattern: /dojo/i, + }, + { + label: "Marko", + pattern: /marko/i, + }, + { + label: "NuxtJS", + pattern: /[\._]nuxt/i, + }, + { + label: "Aframe", + pattern: /aframe/i, + }, + { + label: "NextJS", + pattern: /[\._]next/i, + }, +]; + +export function getLibraryFromUrl( + frame: Frame, + callStack: Array<Frame> = [] +): ?string | void { + // @TODO each of these fns calls getFrameUrl, just call it once + // (assuming there's not more complex logic to identify a lib) + const frameUrl = getFrameUrl(frame); + + // Let's first check if the frame match a defined pattern. + let match = libraryMap.find(o => o.pattern.test(frameUrl)); + if (match) { + return match.label; + } + + // If it does not, it might still be one of the case where the file is used + // by a library but the name has not enough specificity. In such case, we want + // to only return the library name if there are frames matching the library + // pattern in the callStack (e.g. `zone.js` is used by Angular, but the name + // could be quite common and return false positive if evaluated alone. So we + // only return Angular if there are other frames matching Angular). + match = libraryMap.find( + o => o.contextPattern && o.contextPattern.test(frameUrl) + ); + if (match) { + const contextMatch = callStack.some(f => { + const url = getFrameUrl(f); + if (!url) { + return false; + } + + return libraryMap.some(o => o.pattern.test(url)); + }); + + if (contextMatch) { + return match.label; + } + } + + return null; +} diff --git a/devtools/client/debugger/src/utils/pause/frames/index.js b/devtools/client/debugger/src/utils/pause/frames/index.js new file mode 100644 index 0000000000..68d214162c --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/index.js @@ -0,0 +1,11 @@ +/* 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 + +export * from "./annotateFrames"; +export * from "./collapseFrames"; +export * from "./displayName"; +export * from "./getFrameUrl"; +export * from "./getLibraryFromUrl"; diff --git a/devtools/client/debugger/src/utils/pause/frames/moz.build b/devtools/client/debugger/src/utils/pause/frames/moz.build new file mode 100644 index 0000000000..5bb330a57f --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/moz.build @@ -0,0 +1,15 @@ +# vim: set filetype=python: +# 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/. + +DIRS += [] + +CompiledModules( + "annotateFrames.js", + "collapseFrames.js", + "displayName.js", + "getFrameUrl.js", + "getLibraryFromUrl.js", + "index.js", +) diff --git a/devtools/client/debugger/src/utils/pause/frames/tests/__snapshots__/collapseFrames.spec.js.snap b/devtools/client/debugger/src/utils/pause/frames/tests/__snapshots__/collapseFrames.spec.js.snap new file mode 100644 index 0000000000..89dbccd374 --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/tests/__snapshots__/collapseFrames.spec.js.snap @@ -0,0 +1,57 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`collapseFrames default 1`] = ` +Array [ + Object { + "displayName": "a", + }, + Array [ + Object { + "displayName": "b", + "library": "React", + }, + Object { + "displayName": "c", + "library": "React", + }, + ], +] +`; + +exports[`collapseFrames promises 1`] = ` +Array [ + Object { + "displayName": "a", + }, + Array [ + Object { + "displayName": "b", + "library": "React", + }, + Object { + "displayName": "c", + "library": "React", + }, + ], + Object { + "asyncCause": "promise callback", + "displayName": "d", + "library": undefined, + }, + Array [ + Object { + "displayName": "e", + "library": "React", + }, + Object { + "displayName": "f", + "library": "React", + }, + ], + Object { + "asyncCause": null, + "displayName": "g", + "library": undefined, + }, +] +`; diff --git a/devtools/client/debugger/src/utils/pause/frames/tests/annotateFrames.spec.js b/devtools/client/debugger/src/utils/pause/frames/tests/annotateFrames.spec.js new file mode 100644 index 0000000000..752f470e2f --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/tests/annotateFrames.spec.js @@ -0,0 +1,24 @@ +/* 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 + +import { annotateFrames } from "../annotateFrames"; +import { makeMockFrameWithURL } from "../../../test-mockup"; + +describe("annotateFrames", () => { + it("should return Angular", () => { + const callstack = [ + makeMockFrameWithURL( + "https://stackblitz.io/turbo_modules/@angular/core@7.2.4/bundles/core.umd.js" + ), + makeMockFrameWithURL("/node_modules/zone/zone.js"), + makeMockFrameWithURL( + "https://cdnjs.cloudflare.com/ajax/libs/angular/angular.js" + ), + ]; + const frames = annotateFrames(callstack); + expect(frames).toEqual(callstack.map(f => ({ ...f, library: "Angular" }))); + }); +}); diff --git a/devtools/client/debugger/src/utils/pause/frames/tests/collapseFrames.spec.js b/devtools/client/debugger/src/utils/pause/frames/tests/collapseFrames.spec.js new file mode 100644 index 0000000000..15210e0437 --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/tests/collapseFrames.spec.js @@ -0,0 +1,37 @@ +/* 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/>. */ + +import { collapseFrames } from "../collapseFrames"; + +describe("collapseFrames", () => { + it("default", () => { + const groups = collapseFrames([ + { displayName: "a" }, + + { displayName: "b", library: "React" }, + { displayName: "c", library: "React" }, + ]); + + expect(groups).toMatchSnapshot(); + }); + + it("promises", () => { + const groups = collapseFrames([ + { displayName: "a" }, + + { displayName: "b", library: "React" }, + { displayName: "c", library: "React" }, + { + displayName: "d", + library: undefined, + asyncCause: "promise callback", + }, + { displayName: "e", library: "React" }, + { displayName: "f", library: "React" }, + { displayName: "g", library: undefined, asyncCause: null }, + ]); + + expect(groups).toMatchSnapshot(); + }); +}); diff --git a/devtools/client/debugger/src/utils/pause/frames/tests/displayName.spec.js b/devtools/client/debugger/src/utils/pause/frames/tests/displayName.spec.js new file mode 100644 index 0000000000..a362885e1d --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/tests/displayName.spec.js @@ -0,0 +1,129 @@ +/* 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 + +import { + formatCopyName, + formatDisplayName, + simplifyDisplayName, +} from "../displayName"; + +import { makeMockFrame, makeMockSource } from "../../../test-mockup"; + +describe("formatCopyName", () => { + it("simple", () => { + const source = makeMockSource("todo-view.js"); + const frame = makeMockFrame(undefined, source, undefined, 12, "child"); + + expect(formatCopyName(frame, L10N)).toEqual("child (todo-view.js#12)"); + }); +}); + +describe("formatting display names", () => { + it("uses a library description", () => { + const source = makeMockSource("assets/backbone.js"); + const frame = { + ...makeMockFrame(undefined, source, undefined, undefined, "extend/child"), + library: "Backbone", + }; + + expect(formatDisplayName(frame, undefined, L10N)).toEqual("Create Class"); + }); + + it("shortens an anonymous function", () => { + const source = makeMockSource("assets/bar.js"); + const frame = makeMockFrame( + undefined, + source, + undefined, + undefined, + "extend/child/bar/baz" + ); + + expect(formatDisplayName(frame, undefined, L10N)).toEqual("baz"); + }); + + it("does not truncates long function names", () => { + const source = makeMockSource("extend/child/bar/baz"); + const frame = makeMockFrame( + undefined, + source, + undefined, + undefined, + "bazbazbazbazbazbazbazbazbazbazbazbazbaz" + ); + + expect(formatDisplayName(frame, undefined, L10N)).toEqual( + "bazbazbazbazbazbazbazbazbazbazbazbazbaz" + ); + }); + + it("returns the original function name when present", () => { + const source = makeMockSource("entry.js"); + const frame = { + ...makeMockFrame(undefined, source), + originalDisplayName: "originalFn", + displayName: "fn", + }; + + expect(formatDisplayName(frame, undefined, L10N)).toEqual("originalFn"); + }); + + it("returns anonymous when displayName is undefined", () => { + const frame = { ...makeMockFrame(), displayName: undefined }; + expect(formatDisplayName(frame, undefined, L10N)).toEqual("<anonymous>"); + }); + + it("returns anonymous when displayName is null", () => { + const frame = { ...makeMockFrame(), displayName: (null: any) }; + expect(formatDisplayName(frame, undefined, L10N)).toEqual("<anonymous>"); + }); + + it("returns anonymous when displayName is an empty string", () => { + const frame = { ...makeMockFrame(), displayName: "" }; + expect(formatDisplayName(frame, undefined, L10N)).toEqual("<anonymous>"); + }); +}); + +describe("simplifying display names", () => { + const cases = { + defaultCase: [["define", "define"]], + + objectProperty: [ + ["z.foz", "foz"], + ["z.foz/baz", "baz"], + ["z.foz/baz/y.bay", "bay"], + ["outer/x.fox.bax.nx", "nx"], + ["outer/fow.baw", "baw"], + ["fromYUI._attach", "_attach"], + ["Y.ClassNameManager</getClassName", "getClassName"], + ["orion.textview.TextView</addHandler", "addHandler"], + ["this.eventPool_.createObject", "createObject"], + ], + + arrayProperty: [ + ["this.eventPool_[createObject]", "createObject"], + ["jQuery.each(^)/jQuery.fn[o]", "o"], + ["viewport[get+D]", "get+D"], + ["arr[0]", "0"], + ], + + functionProperty: [ + ["fromYUI._attach/<.", "_attach"], + ["Y.ClassNameManager<", "ClassNameManager"], + ["fromExtJS.setVisible/cb<", "cb"], + ["fromDojo.registerWin/<", "registerWin"], + ], + + annonymousProperty: [["jQuery.each(^)", "each"]], + }; + + Object.keys(cases).forEach(type => { + cases[type].forEach(([kase, expected]) => { + it(`${type} - ${kase}`, () => + expect(simplifyDisplayName(kase)).toEqual(expected)); + }); + }); +}); diff --git a/devtools/client/debugger/src/utils/pause/frames/tests/getLibraryFromUrl.spec.js b/devtools/client/debugger/src/utils/pause/frames/tests/getLibraryFromUrl.spec.js new file mode 100644 index 0000000000..afb8d7afc2 --- /dev/null +++ b/devtools/client/debugger/src/utils/pause/frames/tests/getLibraryFromUrl.spec.js @@ -0,0 +1,129 @@ +/* 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 + +import { getLibraryFromUrl } from "../getLibraryFromUrl"; +import { makeMockFrameWithURL } from "../../../test-mockup"; + +describe("getLibraryFromUrl", () => { + describe("When Preact is on the frame", () => { + it("should return Preact and not React", () => { + const frame = makeMockFrameWithURL( + "https://cdnjs.cloudflare.com/ajax/libs/preact/8.2.5/preact.js" + ); + expect(getLibraryFromUrl(frame)).toEqual("Preact"); + }); + }); + + describe("When Vue is on the frame", () => { + it("should return VueJS for different builds", () => { + const buildTypeList = [ + "vue.js", + "vue.common.js", + "vue.esm.js", + "vue.runtime.js", + "vue.runtime.common.js", + "vue.runtime.esm.js", + "vue.min.js", + "vue.runtime.min.js", + ]; + + buildTypeList.forEach(buildType => { + const frame = makeMockFrameWithURL( + `https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/${buildType}` + ); + expect(getLibraryFromUrl(frame)).toEqual("VueJS"); + }); + }); + }); + + describe("When React is in the URL", () => { + it("should not return React if it is not part of the filename", () => { + const notReactUrlList = [ + "https://react.js.com/test.js", + "https://debugger-example.com/test.js", + "https://debugger-react-example.com/test.js", + "https://debugger-react-example.com/react/test.js", + "https://debugger-example.com/react-contextmenu.js", + ]; + notReactUrlList.forEach(notReactUrl => { + const frame = makeMockFrameWithURL(notReactUrl); + expect(getLibraryFromUrl(frame)).toBeNull(); + }); + }); + it("should return React if it is part of the filename", () => { + const reactUrlList = [ + "https://debugger-example.com/react.js", + "https://debugger-example.com/react.development.js", + "https://debugger-example.com/react.production.min.js", + "https://debugger-react-example.com/react.js", + "https://debugger-react-example.com/react/react.js", + "https://debugger-example.com/react-dom.js", + "https://debugger-example.com/react-dom.development.js", + "https://debugger-example.com/react-dom.production.min.js", + "https://debugger-react-example.com/react-dom.js", + "https://debugger-react-example.com/react/react-dom.js", + "https://debugger-react-example.com/react-dom-dev.js", + "/node_modules/react/test.js", + "/node_modules/react-dev/test.js", + "/node_modules/react-dom/test.js", + "/node_modules/react-dom-dev/test.js", + ]; + reactUrlList.forEach(reactUrl => { + const frame = makeMockFrameWithURL(reactUrl); + expect(getLibraryFromUrl(frame)).toEqual("React"); + }); + }); + }); + + describe("When Angular is in the URL", () => { + it("should return Angular for AngularJS (1.x)", () => { + const frame = makeMockFrameWithURL( + "https://cdnjs.cloudflare.com/ajax/libs/angular/angular.js" + ); + expect(getLibraryFromUrl(frame)).toEqual("Angular"); + }); + + it("should return Angular for Angular (2.x)", () => { + const frame = makeMockFrameWithURL( + "https://stackblitz.io/turbo_modules/@angular/core@7.2.4/bundles/core.umd.js" + ); + expect(getLibraryFromUrl(frame)).toEqual("Angular"); + }); + + it("should not return Angular for Angular components", () => { + const frame = makeMockFrameWithURL( + "https://firefox-devtools-angular-log.stackblitz.io/~/src/app/hello.component.ts" + ); + expect(getLibraryFromUrl(frame)).toBeNull(); + }); + }); + + describe("When zone.js is on the frame", () => { + it("should not return Angular when no callstack", () => { + const frame = makeMockFrameWithURL("/node_modules/zone/zone.js"); + expect(getLibraryFromUrl(frame)).toBeNull(); + }); + + it("should not return Angular when stack without Angular frames", () => { + const frame = makeMockFrameWithURL("/node_modules/zone/zone.js"); + const callstack = [frame]; + + expect(getLibraryFromUrl(frame, callstack)).toBeNull(); + }); + + it("should return Angular when stack with AngularJS (1.x) frames", () => { + const frame = makeMockFrameWithURL("/node_modules/zone/zone.js"); + const callstack = [ + frame, + makeMockFrameWithURL( + "https://cdnjs.cloudflare.com/ajax/libs/angular/angular.js" + ), + ]; + + expect(getLibraryFromUrl(frame, callstack)).toEqual("Angular"); + }); + }); +}); |