Skip to content

Commit

Permalink
Merge pull request #8 from dnv-opensource/fixes
Browse files Browse the repository at this point in the history
fixed calculation of absolute and relative path dynamic imports
  • Loading branch information
michaelgoeke authored Jan 14, 2025
2 parents b96799f + bcbd7cf commit 494b47a
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 133 deletions.
25 changes: 12 additions & 13 deletions src/browser/PW_live.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ function updateTooltipPosition(x, y) {

window.mousemove_updateToolTip_running = false;
function mousemove_updateTooltip(event) {
if (mousemove_updateToolTip_running === true) return; //exit early so we don't sawmp the CPU
if (mousemove_updateToolTip_running === true) return; //exit early so we don't swamp the CPU
try {
mousemove_updateToolTip_running = true;
const element = document.elementFromPoint(event.x, event.y);
Expand Down Expand Up @@ -185,7 +185,7 @@ window.addEventListener("click", recordModeClickHandler, true);
/******** page object model feature ********/

window.navigation.onnavigatesuccess = async () => await reload_page_object_model_elements();
window.setInterval(async () => await reload_page_object_model_elements(), 5000); //refresh the page object model highlighting every 5 seconds in case on-screen elements have changed
//window.setInterval(async () => await reload_page_object_model_elements(), 5000); //refresh the page object model highlighting every 5 seconds in case on-screen elements have changed


var pageObjectFilePath = "";
Expand All @@ -202,28 +202,21 @@ async function reload_page_object_model_elements() {
const pageObject = window.PW_pages[pageObjectFilePath];
if (pageObject === undefined) return;

const propertyRegex = new RegExp(config.pageObjectModel.propertySelectorRegex.slice(1, -1));
const pageObjectModelImportStatement = await PW_importStatement(pageObject.className, pageObjectFilePath);
for (var prop in pageObject.page) {
for (var prop of pageObject.selectors) {
try {
const selectorMethodName = propertyRegex.exec(prop)?.[1];
if (!selectorMethodName) continue;

const selector = pageObject.page[prop];
const matchingElements = playwright.locator(selector).elements;
const matchingElements = playwright.locator(prop.selector).elements;
if (matchingElements.length > 1) {
//todo: show a warning somehow
}
if (matchingElements.length === 0) {
console.info(`could not find element for selector ${selector}. skipping.`);
console.info(`could not find element for selector ${prop.selector}. skipping.`);
continue;
}

const selectorMethod = "" + pageObject.page[selectorMethodName].toString();
const selectorMethodArgs = selectorMethod.slice(selectorMethod.indexOf("("), selectorMethod.indexOf(")") + 1);
const primaryAction = config.pageObjectModel.primaryActionByCssSelector.find(([css]) => matchingElements[0].matches(css))[1];
const secondaryActions = config.pageObjectModel.secondaryActionByCssSelector.filter(([css]) => matchingElements[0].matches(css)).map(([, action]) => action);
const dataPageObjectModel = `${pageObject.className}.${selectorMethodName}${selectorMethodArgs}`;
const dataPageObjectModel = `${pageObject.className}.${prop.selectorMethod.name}(${prop.selectorMethod.args.join(', ')})`;
for (const el of matchingElements) {
el.setAttribute("data-page-object-model", dataPageObjectModel);
el.setAttribute("data-page-object-model-import", pageObjectModelImportStatement);
Expand All @@ -241,6 +234,12 @@ async function reload_page_object_model_elements() {

function clearPageObjectModelElements() {
if (window.PW_overlays !== undefined) for (const el of window.PW_overlays) config.pageObjectModel.overlay.off(el);
//clean up any rogue elements
const pageObjectModelAttributes = ['data-page-object-model', 'data-page-object-model-import', 'data-page-object-model-primary-action', 'data-page-object-model-secondary-actions'];
document.querySelectorAll(pageObjectModelAttributes.join(', ')).forEach(el => {
pageObjectModelAttributes.forEach(attr => el.removeAttribute(attr));
config.pageObjectModel.overlay.off(el)
});
window.PW_overlays = [];
}

Expand Down
14 changes: 8 additions & 6 deletions src/hotModuleReload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ export module hotModuleReload {
const blockToExecute = _getBlockToExecute(s.testFnContents, newTestFnContents);
if (blockToExecute === '')
return;
await evalLines(blockToExecute, s);
await evalLines(blockToExecute);
s.testFnContents = newTestFnContents;
} finally {
release();
}
});
}

async function evalLines(lines: string, s: hotModuleReloadState) {
const importsBlock = _rewriteAsDynamicImports(s.imports).join('\n');
async function evalLines(lines: string) {
const importsBlock = _rewriteAsDynamicImports(_state.imports).join('\n');
const wrappedEvalLines = _wrapAsyncAsPromise(importsBlock + '\n\n' + lines, _extractVariableListFrom(lines));
await _evalCore(s.evalScope, s.pageEvaluate, [wrappedEvalLines]);
return _evalCore(_state.evalScope, _state.pageEvaluate, wrappedEvalLines);
}

function _rewriteAsDynamicImports(imports: ImportDeclaration[])
Expand Down Expand Up @@ -91,10 +91,11 @@ ${variables.length === 0 ? `` : `Object.assign(globalThis, { ${variables.join(',
return variableNames.flat();
}

export async function _evalCore(evalScope: (s: string) => any, pageEvaluate: (pageFunction: string) => Promise<unknown>, codeBlocks: string[]) {
export async function _evalCore(evalScope: (s: string) => any, pageEvaluate: (pageFunction: string) => Promise<unknown>, codeBlock: string) {
let result;
try {
await pageEvaluate(`window.PW_executing = true`);
await evalScope(codeBlocks.join(''));
result = await evalScope(codeBlock);
await pageEvaluate(`PW_reportError()`);
} catch (error) {
if (error instanceof Error) {
Expand All @@ -107,6 +108,7 @@ ${variables.length === 0 ? `` : `Object.assign(globalThis, { ${variables.join(',
} finally {
await pageEvaluate(`window.PW_executing = false; window.reload_page_object_model_elements();`);
}
return result;
}

export function _extractImports(filename: string) {
Expand Down
16 changes: 8 additions & 8 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { hotModuleReload } from "./hotModuleReload";
import { pageObjectModel } from "./pageObjectModel";
import { getTestCallingLocation } from "./utility";
import fs from 'fs/promises';
import process from 'node:process';
import { ts } from "ts-morph";
import { PlaywrightLiveRecorderConfig, PlaywrightLiveRecorderConfigFile } from "./types";

//github copilot directive: for the defaultConfig property - add inline jsdoc @default attributes for each property below, have the @default value match the actual values of each property
import { PlaywrightLiveRecorderConfig, PlaywrightLiveRecorderConfigFile, PlaywrightLiveRecorderConfig_recorder, PlaywrightLiveRecorderConfig_pageObjectModel, PlaywrightLiveRecorderConfig_diagnostic, TestCallingLocation } from "./types";
export { PlaywrightLiveRecorderConfig, PlaywrightLiveRecorderConfigFile, PlaywrightLiveRecorderConfig_recorder, PlaywrightLiveRecorderConfig_pageObjectModel, PlaywrightLiveRecorderConfig_diagnostic, TestCallingLocation };

export module PlaywrightLiveRecorder {
export const defaultConfig: PlaywrightLiveRecorderConfig = {
Expand Down Expand Up @@ -126,11 +126,11 @@ export class ${className} {
pageState.PlaywrightLiveRecorder_started = true;

const isHeadless = test.info().project.use.headless;
if (isHeadless !== false) {
console.error('startLiveCoding called while running headless');
const pwdebug = process.env.PWDEBUG == 'console';
if (isHeadless !== false && !pwdebug) {
console.error('startLiveCoding called while running headless or env variable PWDEBUG=console not set');
return;
}

config = _mergeConfig(defaultConfig, await _configFromFile(), configOverrides);
if (!config.pageObjectModel.path.endsWith('/')) config.pageObjectModel.path +='/';

Expand All @@ -140,7 +140,7 @@ export class ${className} {
await testFileWriter.init(page, testCallingLocation);

await hotModuleReload.init(testCallingLocation, config.pageObjectModel.importerCustomizationHooks, (str: string) => page.evaluate(str), evalScope);
await page.exposeFunction('PW_eval', (codeBlocks: string[]) => hotModuleReload._evalCore(evalScope, s => page.evaluate(s), codeBlocks));
await page.exposeFunction('PW_eval', (codeBlock: string) => hotModuleReload._evalCore(evalScope, s => page.evaluate(s), codeBlock));

await recorder.init(config.recorder, page);

Expand All @@ -150,7 +150,7 @@ export class ${className} {

if (config.pageObjectModel.enabled) {
config.pageObjectModel.baseUrl = config.pageObjectModel.baseUrl ?? test.info().project.use.baseURL!;
await pageObjectModel.init(nodePath.dirname(testCallingLocation.file), config.pageObjectModel, page);
await pageObjectModel.init(nodePath.dirname(testCallingLocation.file), config.pageObjectModel, evalScope, page);
}

page.on('load', async page => {
Expand Down
Loading

0 comments on commit 494b47a

Please sign in to comment.