diff options
Diffstat (limited to 'devtools/client/debugger/src/utils/test-head.js')
-rw-r--r-- | devtools/client/debugger/src/utils/test-head.js | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/utils/test-head.js b/devtools/client/debugger/src/utils/test-head.js new file mode 100644 index 0000000000..270b86f5ac --- /dev/null +++ b/devtools/client/debugger/src/utils/test-head.js @@ -0,0 +1,301 @@ +/* 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 + +/** + * Utils for Jest + * @module utils/test-head + */ + +import { combineReducers, type Store } from "redux"; +import sourceMaps from "devtools-source-map"; +import reducers from "../reducers"; +import actions from "../actions"; +import * as selectors from "../selectors"; +import { parserWorker, evaluationsParser } from "../test/tests-setup"; +import configureStore from "../actions/utils/create-store"; +import sourceQueue from "../utils/source-queue"; +import type { + ThreadContext, + Source, + OriginalSourceData, + GeneratedSourceData, +} from "../types"; +import type { State } from "../reducers/types"; +import type { Action } from "../actions/types"; + +type TestStore = Store<State, Action, any> & { + thunkArgs: () => { + dispatch: any, + getState: () => State, + client: any, + sourceMaps: any, + panel: {||}, + }, + cx: ThreadContext, +}; + +/** + * This file contains older interfaces used by tests that have not been + * converted to use test-mockup.js + */ + +/** + * @memberof utils/test-head + * @static + */ +function createStore( + client: any, + initialState: any = {}, + sourceMapsMock: any +): TestStore { + const store: any = configureStore({ + log: false, + makeThunkArgs: args => { + return { + ...args, + client, + sourceMaps: sourceMapsMock !== undefined ? sourceMapsMock : sourceMaps, + parser: parserWorker, + evaluationsParser, + }; + }, + })(combineReducers(reducers), initialState); + sourceQueue.clear(); + sourceQueue.initialize({ + newQueuedSources: sources => + store.dispatch(actions.newQueuedSources(sources)), + }); + + store.thunkArgs = () => ({ + dispatch: store.dispatch, + getState: store.getState, + client, + sourceMaps, + panel: {}, + }); + + // Put the initial context in the store, for convenience to unit tests. + store.cx = selectors.getThreadContext(store.getState()); + + return store; +} + +/** + * @memberof utils/test-head + * @static + */ +function commonLog(msg: string, data: any = {}) { + console.log(`[INFO] ${msg} ${JSON.stringify(data)}`); +} + +function makeFrame({ id, sourceId, thread }: Object, opts: Object = {}) { + return { + id, + scope: { bindings: { variables: {}, arguments: [] } }, + location: { sourceId, line: 4 }, + thread: thread || "FakeThread", + ...opts, + }; +} + +function createSourceObject( + filename: string, + props: { + isBlackBoxed?: boolean, + } = {} +): Source { + return ({ + id: filename, + url: makeSourceURL(filename), + isBlackBoxed: !!props.isBlackBoxed, + isPrettyPrinted: false, + isExtension: false, + isOriginal: filename.includes("originalSource"), + }: any); +} + +function createOriginalSourceObject(generated: Source): Source { + const rv = { + ...generated, + id: `${generated.id}/originalSource`, + }; + + return (rv: any); +} + +function makeSourceURL(filename: string) { + return `http://localhost:8000/examples/${filename}`; +} + +type MakeSourceProps = { + sourceMapBaseURL?: string, + sourceMapURL?: string, + introductionType?: string, + isBlackBoxed?: boolean, +}; +function createMakeSource(): ( + // The name of the file that this actor is part of. + name: string, + props?: MakeSourceProps +) => GeneratedSourceData { + const indicies = {}; + + return function(name, props = {}) { + const index = (indicies[name] | 0) + 1; + indicies[name] = index; + + return { + id: name, + thread: "FakeThread", + source: { + actor: `${name}-${index}-actor`, + url: `http://localhost:8000/examples/${name}`, + sourceMapBaseURL: props.sourceMapBaseURL || null, + sourceMapURL: props.sourceMapURL || null, + introductionType: props.introductionType || null, + isBlackBoxed: !!props.isBlackBoxed, + extensionName: null, + }, + isServiceWorker: false, + }; + }; +} + +/** + * @memberof utils/test-head + * @static + */ +let creator; +beforeEach(() => { + creator = createMakeSource(); +}); +afterEach(() => { + creator = null; +}); +function makeSource(name: string, props?: MakeSourceProps) { + if (!creator) { + throw new Error("makeSource() cannot be called outside of a test"); + } + + return creator(name, props); +} + +function makeOriginalSource(source: Source): OriginalSourceData { + return { + id: `${source.id}/originalSource`, + url: `${source.url}-original`, + }; +} + +function makeFuncLocation(startLine, endLine) { + if (!endLine) { + endLine = startLine + 1; + } + return { + start: { + line: startLine, + }, + end: { + line: endLine, + }, + }; +} + +function makeSymbolDeclaration( + name: string, + start: number, + end: ?number, + klass: ?string +) { + return { + id: `${name}:${start}`, + name, + location: makeFuncLocation(start, end), + klass, + }; +} + +/** + * @memberof utils/test-head + * @static + */ +function waitForState(store: any, predicate: any): Promise<void> { + return new Promise(resolve => { + let ret = predicate(store.getState()); + if (ret) { + resolve(ret); + } + + const unsubscribe = store.subscribe(() => { + ret = predicate(store.getState()); + if (ret) { + unsubscribe(); + // NOTE: memoizableAction adds an additional tick for validating context + setTimeout(() => resolve(ret)); + } + }); + }); +} + +function watchForState(store: any, predicate: any): () => boolean { + let sawState = false; + const checkState = function() { + if (!sawState && predicate(store.getState())) { + sawState = true; + } + return sawState; + }; + + let unsubscribe; + if (!checkState()) { + unsubscribe = store.subscribe(() => { + if (checkState()) { + unsubscribe(); + } + }); + } + + return function read() { + if (unsubscribe) { + unsubscribe(); + } + + return sawState; + }; +} + +function getTelemetryEvents(eventName: string) { + return window.dbg._telemetry.events[eventName] || []; +} + +function waitATick(callback: Function): Promise<*> { + return new Promise(resolve => { + setTimeout(() => { + callback(); + resolve(); + }); + }); +} + +export { + actions, + selectors, + reducers, + createStore, + commonLog, + getTelemetryEvents, + makeFrame, + createSourceObject, + createOriginalSourceObject, + createMakeSource, + makeSourceURL, + makeSource, + makeOriginalSource, + makeSymbolDeclaration, + waitForState, + watchForState, + waitATick, +}; |