UNPKG

5.99 kBJavaScriptView Raw
1
2"use strict";
3
4const { sessionType } = require('../commons/constants');
5const perfLogger = require('../commons/performance-logger');
6const MemoryFS = require('memory-fs');
7const mfs = new MemoryFS();
8const AbortController = require("abort-controller");
9
10/**
11 * @type {Map<string, import("abort-controller")>}
12 */
13const runningStepsAbortControllersRegistry = new Map();
14
15// TODO(Benji) cache compile between runs with webpack cache plugin or runnerFileCache
16
17let stepsProxy = undefined;
18
19module.exports.run = (driver, remoteStep, projectData, userData) => {
20 const { step, context} = remoteStep.data;
21 return module.exports.execute(step, context, driver, userData.loginData);
22 // (step, context, driver, loginData, frameManager)
23}
24
25// this function is exported as a "make sense API", `run` above is teh remote run signature for polymorphic dispatch
26module.exports.execute = async function execute(step, context, driver, loginData, frameManager, source = 'cli') {
27 const abortController = new AbortController();
28 const { signal: abortSignal } = abortController;
29
30 runningStepsAbortControllersRegistry.set(context.stepResultId, abortController);
31
32 try {
33 perfLogger.log('before seleniumTestPlayer require');
34 const SeleniumTestPlayer = require('../player/seleniumTestPlayer');
35 const { compileFunctionsLibrary } = require('../agent/routers/codim/service');
36
37 const { functionName } = step;
38 const userParamsData = {}; // no tdk access
39 //TODO (benji) read function parameters and arguments from context.incomingParams.as
40 const shouldMonitorPerformance = false;
41 const player = new SeleniumTestPlayer(
42 context.id, // has to be on the same session ID to reuse the same tabs from tabService
43 userParamsData,
44 shouldMonitorPerformance,
45 sessionType.CODEFUL,
46 driver
47 );
48
49 stepsProxy = player.sessionPlayer.codeSessionPlayer.proxy;
50 // reuse the same memory filesystem between runs for cache
51
52 const bypassWebpack = step.bypassWebpack ? { testim: stepsProxy.wrappedSteps() } : false;
53
54 perfLogger.log('before compileFunctionsLibrary', {bypassWebpack: Boolean(step.bypassWebpack) });
55 let code;
56 try {
57 code = await compileFunctionsLibrary({ fileSystem: mfs, bypassWebpack }, abortSignal);
58 } catch (e) {
59 return { success: false, shouldRetry: false, reason: 'Unable to compile functions library. ' + e.message, extraInfo: e.stack };
60 }
61
62 if (typeof globalThis === 'undefined') { // fix for Node 8 and Node 10
63 global.globalThis = process;
64 }
65 let hybridFunction;
66 if (!bypassWebpack) {
67 global.globalThis.__testim = stepsProxy.wrappedSteps();
68 // this evaluates the code while exposing __testim. We manually expose the proxy here since
69 // our test-extractor code assumes it's extracting tests and this just needs to "eval" the whole file
70 (0, eval)(code);
71 hybridFunction = globalThis.tdk[functionName];
72 } else {
73 hybridFunction = code[functionName];
74 }
75
76 perfLogger.log('after hybridFunction obtain and eval');
77 if (!hybridFunction) {
78 return {
79 success: false,
80 shouldRetry: false,
81 reason: `Could not find function '${functionName}' locally. Please make sure you have a functions.js file with a '${functionName}' function defined`,
82 extraInfo: Object.keys(globalThis.tdk) // log available functions to make debugging easier
83 };
84 }
85
86 if (hybridFunction.type === 'selenium') {
87 const seleniumPlayback = require('./seleniumHybridStepPlayback').execute;
88 return await seleniumPlayback(player, hybridFunction, step, context, source, abortSignal);
89 }
90 if (hybridFunction.type === 'puppeteer') {
91 if (!player.driver.cdpUrl) {
92 return { success: false, shouldRetry: false, reason: 'running puppeteer code requires the remote debugging protocol to be open. Please contact Testim support.'}
93 }
94 perfLogger.log('before puppeteerPlayback');
95 const puppeteerPlayback = require('./puppeteerHybridStepPlayback').execute;
96 try {
97 return await puppeteerPlayback(player, hybridFunction, step, context, source, abortSignal);
98 } finally {
99 perfLogger.log('after puppeteerPlayback');
100 }
101 }
102
103 if (hybridFunction.type === 'playwright') {
104 if (!player.driver.cdpUrl) {
105 return { success: false, shouldRetry: false, reason: 'running playwright code requires the remote debugging protocol to be open. Please contact Testim support.'}
106 }
107 const playwrightPlayback = require('./playwrightHybridStepPlayback').execute;
108 return await playwrightPlayback(player, hybridFunction, step, context, source, abortSignal);
109 }
110
111 if (hybridFunction.type === 'tdk' || !hybridFunction.type) {
112 const tdkPlayback = require('./tdkHybridStepPlayback').execute;
113 perfLogger.log('before tdkPlayback');
114 try {
115 return await tdkPlayback(player, hybridFunction, context, loginData, frameManager, step, source, abortSignal);
116 } finally {
117 perfLogger.log('after tdkPlayback');
118 }
119 }
120
121 return { success: false, shouldRetry: false, reason: 'unknown hybrid format ' + hybridFunction.type };
122 } finally {
123 runningStepsAbortControllersRegistry.delete(context.stepResultId);
124 }
125}
126
127module.exports.abort = function abort(stepResultId) {
128 const abortController = runningStepsAbortControllersRegistry.get(stepResultId);
129
130 if (abortController) {
131 abortController.abort();
132 } else {
133 throw new Error("No such stepResultId");
134 }
135}