mirror of
https://github.com/checkly/headless-recorder.git
synced 2021-07-28 02:03:42 +03:00
chore: update prettier defaults and use latest version of finder
This commit is contained in:
30
.eslintrc.js
30
.eslintrc.js
@@ -1,15 +1,31 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
|
||||
env: {
|
||||
node: true,
|
||||
webextensions: true
|
||||
webextensions: true,
|
||||
},
|
||||
extends: ["plugin:vue/vue3-essential", "eslint:recommended", "@vue/prettier"],
|
||||
|
||||
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/prettier'],
|
||||
|
||||
parserOptions: {
|
||||
parser: "babel-eslint"
|
||||
parser: 'babel-eslint',
|
||||
},
|
||||
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
|
||||
}
|
||||
};
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
},
|
||||
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'**/__tests__/*.{j,t}s?(x)',
|
||||
'**/tests/unit/**/*.spec.{j,t}s?(x)',
|
||||
],
|
||||
env: {
|
||||
jest: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
6
.prettierrc.js
Normal file
6
.prettierrc.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
tabWidth: 2,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"]
|
||||
};
|
||||
presets: ['@vue/cli-plugin-babel/preset'],
|
||||
}
|
||||
|
||||
51
package.json
51
package.json
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"name": "headless-recorder",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service build --mode development --watch",
|
||||
"build": "vue-cli-service build",
|
||||
@@ -9,7 +8,7 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@medv/finder": "1.1.2",
|
||||
"@medv/finder": "2.0.0",
|
||||
"@tailwindcss/postcss7-compat": "2.0.2",
|
||||
"@vueuse/core": "4.0.8",
|
||||
"autoprefixer": "9",
|
||||
@@ -17,31 +16,31 @@
|
||||
"postcss": "7",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@2.0.2",
|
||||
"vue": "3.0.6",
|
||||
"vue3-highlightjs": "1.0.5"
|
||||
"vue3-highlightjs": "^1.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "5.12.0",
|
||||
"@vue/cli-plugin-babel": "4.5.0",
|
||||
"@vue/cli-plugin-eslint": "4.5.0",
|
||||
"@vue/cli-plugin-unit-jest": "4.5.12",
|
||||
"@vue/cli-service": "4.5.0",
|
||||
"@vue/compiler-sfc": "3.0.0",
|
||||
"@vue/eslint-config-prettier": "6.0.0",
|
||||
"@vue/test-utils": "2.0.0-rc.6",
|
||||
"babel-eslint": "10.1.0",
|
||||
"eslint": "6.7.2",
|
||||
"eslint-plugin-prettier": "3.1.3",
|
||||
"eslint-plugin-vue": "7.10.0",
|
||||
"jest": "26.6.3",
|
||||
"jest-vue-preprocessor": "1.7.1",
|
||||
"node-sass": "5.0.0",
|
||||
"playwright": "1.10.0",
|
||||
"prettier": "1.19.1",
|
||||
"puppeteer": "9.0.0",
|
||||
"sass-loader": "10.1.1",
|
||||
"typescript": "3.9.3",
|
||||
"vue-cli-plugin-browser-extension": "0.25.1",
|
||||
"vue-cli-plugin-tailwind": "2.0.6",
|
||||
"vue-jest": "5.0.0-alpha.9"
|
||||
"@testing-library/jest-dom": "^5.12.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-unit-jest": "^4.5.12",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/compiler-sfc": "^3.0.0",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/test-utils": "^2.0.0-rc.6",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-vue": "^7.0.0-0",
|
||||
"jest": "^26.6.3",
|
||||
"jest-vue-preprocessor": "^1.7.1",
|
||||
"node-sass": "^5.0.0",
|
||||
"playwright": "^1.10.0",
|
||||
"prettier": "^1.19.1",
|
||||
"puppeteer": "^9.0.0",
|
||||
"sass-loader": "^10.1.1",
|
||||
"typescript": "~3.9.3",
|
||||
"vue-cli-plugin-browser-extension": "~0.25.1",
|
||||
"vue-cli-plugin-tailwind": "~2.0.6",
|
||||
"vue-jest": "^5.0.0-0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import puppeteer from "puppeteer";
|
||||
import { launchPuppeteerWithExtension } from "./helpers";
|
||||
import puppeteer from 'puppeteer'
|
||||
import { launchPuppeteerWithExtension } from './helpers'
|
||||
|
||||
describe("install", () => {
|
||||
test("it installs the extension", async () => {
|
||||
const browser = await launchPuppeteerWithExtension(puppeteer);
|
||||
expect(browser).toBeTruthy();
|
||||
browser.close();
|
||||
}, 5000);
|
||||
});
|
||||
describe('install', () => {
|
||||
test('it installs the extension', async () => {
|
||||
const browser = await launchPuppeteerWithExtension(puppeteer)
|
||||
expect(browser).toBeTruthy()
|
||||
browser.close()
|
||||
}, 5000)
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import path from "path";
|
||||
import { scripts } from "../../package.json";
|
||||
const util = require("util");
|
||||
const exec = util.promisify(require("child_process").exec);
|
||||
import path from 'path'
|
||||
import { scripts } from '../../package.json'
|
||||
const util = require('util')
|
||||
const exec = util.promisify(require('child_process').exec)
|
||||
|
||||
const extensionPath = path.join(__dirname, "../../dist");
|
||||
const extensionPath = path.join(__dirname, '../../dist')
|
||||
|
||||
export const launchPuppeteerWithExtension = function(puppeteer) {
|
||||
const options = {
|
||||
@@ -13,18 +13,18 @@ export const launchPuppeteerWithExtension = function(puppeteer) {
|
||||
args: [
|
||||
`--disable-extensions-except=${extensionPath}`,
|
||||
`--load-extension=${extensionPath}`,
|
||||
"--no-sandbox",
|
||||
"--disable-setuid-sandbox"
|
||||
]
|
||||
};
|
||||
|
||||
if (process.env.CI) {
|
||||
options.executablePath = process.env.PUPPETEER_EXEC_PATH; // Set by docker on github actions
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
],
|
||||
}
|
||||
|
||||
return puppeteer.launch(options);
|
||||
};
|
||||
if (process.env.CI) {
|
||||
options.executablePath = process.env.PUPPETEER_EXEC_PATH // Set by docker on github actions
|
||||
}
|
||||
|
||||
return puppeteer.launch(options)
|
||||
}
|
||||
|
||||
export const runBuild = function() {
|
||||
return exec(scripts.build);
|
||||
};
|
||||
return exec(scripts.build)
|
||||
}
|
||||
|
||||
@@ -1,163 +1,163 @@
|
||||
import pptrActions from "@/services/pptr-actions";
|
||||
import ctrl from "@/models/extension-control-messages";
|
||||
import actions from "@/models/extension-ui-actions";
|
||||
import pptrActions from '@/services/pptr-actions'
|
||||
import ctrl from '@/models/extension-control-messages'
|
||||
import actions from '@/models/extension-ui-actions'
|
||||
|
||||
class RecordingController {
|
||||
constructor() {
|
||||
this._recording = [];
|
||||
this._boundedMessageHandler = null;
|
||||
this._boundedNavigationHandler = null;
|
||||
this._boundedWaitHandler = null;
|
||||
this._boundedMenuHandler = null;
|
||||
this._boundedKeyCommandHandler = null;
|
||||
this._badgeState = "";
|
||||
this._isPaused = false;
|
||||
this._recording = []
|
||||
this._boundedMessageHandler = null
|
||||
this._boundedNavigationHandler = null
|
||||
this._boundedWaitHandler = null
|
||||
this._boundedMenuHandler = null
|
||||
this._boundedKeyCommandHandler = null
|
||||
this._badgeState = ''
|
||||
this._isPaused = false
|
||||
|
||||
// Some events are sent double on page navigations to simplify the event recorder.
|
||||
// We keep some simple state to disregard events if needed.
|
||||
this._hasGoto = false;
|
||||
this._hasViewPort = false;
|
||||
this._hasGoto = false
|
||||
this._hasViewPort = false
|
||||
|
||||
this._menuId = "PUPPETEER_RECORDER_CONTEXT_MENU";
|
||||
this._menuId = 'PUPPETEER_RECORDER_CONTEXT_MENU'
|
||||
this._menuOptions = {
|
||||
SCREENSHOT: "SCREENSHOT",
|
||||
SCREENSHOT_CLIPPED: "SCREENSHOT_CLIPPED"
|
||||
};
|
||||
SCREENSHOT: 'SCREENSHOT',
|
||||
SCREENSHOT_CLIPPED: 'SCREENSHOT_CLIPPED',
|
||||
}
|
||||
}
|
||||
|
||||
boot() {
|
||||
chrome.extension.onConnect.addListener(port => {
|
||||
console.debug("listeners connected");
|
||||
console.debug('listeners connected')
|
||||
port.onMessage.addListener(msg => {
|
||||
if (msg.action && msg.action === actions.START) this.start();
|
||||
if (msg.action && msg.action === actions.STOP) this.stop();
|
||||
if (msg.action && msg.action === actions.CLEAN_UP) this.cleanUp();
|
||||
if (msg.action && msg.action === actions.PAUSE) this.pause();
|
||||
if (msg.action && msg.action === actions.UN_PAUSE) this.unPause();
|
||||
});
|
||||
});
|
||||
if (msg.action && msg.action === actions.START) this.start()
|
||||
if (msg.action && msg.action === actions.STOP) this.stop()
|
||||
if (msg.action && msg.action === actions.CLEAN_UP) this.cleanUp()
|
||||
if (msg.action && msg.action === actions.PAUSE) this.pause()
|
||||
if (msg.action && msg.action === actions.UN_PAUSE) this.unPause()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
start() {
|
||||
console.debug("start recording");
|
||||
console.debug('start recording')
|
||||
this.cleanUp(() => {
|
||||
this._badgeState = "rec";
|
||||
this._badgeState = 'rec'
|
||||
|
||||
this._hasGoto = false;
|
||||
this._hasViewPort = false;
|
||||
this._hasGoto = false
|
||||
this._hasViewPort = false
|
||||
|
||||
this.injectScript();
|
||||
this.injectScript()
|
||||
|
||||
this._boundedMessageHandler = this.handleMessage.bind(this);
|
||||
this._boundedNavigationHandler = this.handleNavigation.bind(this);
|
||||
this._boundedWaitHandler = this.handleWait.bind(this);
|
||||
this._boundedMessageHandler = this.handleMessage.bind(this)
|
||||
this._boundedNavigationHandler = this.handleNavigation.bind(this)
|
||||
this._boundedWaitHandler = this.handleWait.bind(this)
|
||||
|
||||
chrome.runtime.onMessage.addListener(this._boundedMessageHandler);
|
||||
chrome.runtime.onMessage.addListener(this._boundedMessageHandler)
|
||||
chrome.webNavigation.onCompleted.addListener(
|
||||
this._boundedNavigationHandler
|
||||
);
|
||||
)
|
||||
chrome.webNavigation.onBeforeNavigate.addListener(
|
||||
this._boundedWaitHandler
|
||||
);
|
||||
)
|
||||
|
||||
chrome.browserAction.setIcon({ path: "./images/icon-green.png" });
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState });
|
||||
chrome.browserAction.setBadgeBackgroundColor({ color: "#FF0000" });
|
||||
chrome.browserAction.setIcon({ path: './images/icon-green.png' })
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState })
|
||||
chrome.browserAction.setBadgeBackgroundColor({ color: '#FF0000' })
|
||||
|
||||
/**
|
||||
* Right click menu setup
|
||||
*/
|
||||
|
||||
chrome.contextMenus.removeAll();
|
||||
chrome.contextMenus.removeAll()
|
||||
|
||||
// add the parent and its children
|
||||
|
||||
chrome.contextMenus.create({
|
||||
id: this._menuId,
|
||||
title: "Headless Recorder",
|
||||
contexts: ["all"]
|
||||
});
|
||||
title: 'Headless Recorder',
|
||||
contexts: ['all'],
|
||||
})
|
||||
|
||||
chrome.contextMenus.create({
|
||||
id: this._menuId + this._menuOptions.SCREENSHOT,
|
||||
title: "Take Screenshot (Ctrl+Shift+A)",
|
||||
title: 'Take Screenshot (Ctrl+Shift+A)',
|
||||
parentId: this._menuId,
|
||||
contexts: ["all"]
|
||||
});
|
||||
contexts: ['all'],
|
||||
})
|
||||
|
||||
chrome.contextMenus.create({
|
||||
id: this._menuId + this._menuOptions.SCREENSHOT_CLIPPED,
|
||||
title: "Take Screenshot Clipped (Ctrl+Shift+S)",
|
||||
title: 'Take Screenshot Clipped (Ctrl+Shift+S)',
|
||||
parentId: this._menuId,
|
||||
contexts: ["all"]
|
||||
});
|
||||
contexts: ['all'],
|
||||
})
|
||||
|
||||
// add the handlers
|
||||
|
||||
this._boundedMenuHandler = this.handleMenuInteraction.bind(this);
|
||||
chrome.contextMenus.onClicked.addListener(this._boundedMenuHandler);
|
||||
this._boundedMenuHandler = this.handleMenuInteraction.bind(this)
|
||||
chrome.contextMenus.onClicked.addListener(this._boundedMenuHandler)
|
||||
|
||||
this._boundedKeyCommandHandler = this.handleKeyCommands.bind(this);
|
||||
chrome.commands.onCommand.addListener(this._boundedKeyCommandHandler);
|
||||
});
|
||||
this._boundedKeyCommandHandler = this.handleKeyCommands.bind(this)
|
||||
chrome.commands.onCommand.addListener(this._boundedKeyCommandHandler)
|
||||
})
|
||||
}
|
||||
|
||||
stop() {
|
||||
console.debug("stop recording");
|
||||
this._badgeState = this._recording.length > 0 ? "1" : "";
|
||||
console.debug('stop recording')
|
||||
this._badgeState = this._recording.length > 0 ? '1' : ''
|
||||
|
||||
chrome.runtime.onMessage.removeListener(this._boundedMessageHandler);
|
||||
chrome.runtime.onMessage.removeListener(this._boundedMessageHandler)
|
||||
chrome.webNavigation.onCompleted.removeListener(
|
||||
this._boundedNavigationHandler
|
||||
);
|
||||
)
|
||||
chrome.webNavigation.onBeforeNavigate.removeListener(
|
||||
this._boundedWaitHandler
|
||||
);
|
||||
chrome.contextMenus.onClicked.removeListener(this._boundedMenuHandler);
|
||||
)
|
||||
chrome.contextMenus.onClicked.removeListener(this._boundedMenuHandler)
|
||||
|
||||
chrome.browserAction.setIcon({ path: "./images/icon-black.png" });
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState });
|
||||
chrome.browserAction.setBadgeBackgroundColor({ color: "#45C8F1" });
|
||||
chrome.browserAction.setIcon({ path: './images/icon-black.png' })
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState })
|
||||
chrome.browserAction.setBadgeBackgroundColor({ color: '#45C8F1' })
|
||||
|
||||
chrome.storage.local.set({ recording: this._recording }, () => {
|
||||
console.debug("recording stored");
|
||||
});
|
||||
console.debug('recording stored')
|
||||
})
|
||||
}
|
||||
|
||||
pause() {
|
||||
console.debug("pause");
|
||||
this._badgeState = "❚❚";
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState });
|
||||
this._isPaused = true;
|
||||
console.debug('pause')
|
||||
this._badgeState = '❚❚'
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState })
|
||||
this._isPaused = true
|
||||
}
|
||||
|
||||
unPause() {
|
||||
console.debug("unpause");
|
||||
this._badgeState = "rec";
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState });
|
||||
this._isPaused = false;
|
||||
console.debug('unpause')
|
||||
this._badgeState = 'rec'
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState })
|
||||
this._isPaused = false
|
||||
}
|
||||
|
||||
cleanUp(cb) {
|
||||
console.debug("cleanup");
|
||||
this._recording = [];
|
||||
chrome.browserAction.setBadgeText({ text: "" });
|
||||
chrome.storage.local.remove("recording", () => {
|
||||
console.debug("stored recording cleared");
|
||||
if (cb) cb();
|
||||
});
|
||||
console.debug('cleanup')
|
||||
this._recording = []
|
||||
chrome.browserAction.setBadgeText({ text: '' })
|
||||
chrome.storage.local.remove('recording', () => {
|
||||
console.debug('stored recording cleared')
|
||||
if (cb) cb()
|
||||
})
|
||||
}
|
||||
|
||||
recordCurrentUrl(href) {
|
||||
if (!this._hasGoto) {
|
||||
console.debug("recording goto* for:", href);
|
||||
console.debug('recording goto* for:', href)
|
||||
this.handleMessage({
|
||||
selector: undefined,
|
||||
value: undefined,
|
||||
action: pptrActions.GOTO,
|
||||
href
|
||||
});
|
||||
this._hasGoto = true;
|
||||
href,
|
||||
})
|
||||
this._hasGoto = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,9 +166,9 @@ class RecordingController {
|
||||
this.handleMessage({
|
||||
selector: undefined,
|
||||
value,
|
||||
action: pptrActions.VIEWPORT
|
||||
});
|
||||
this._hasViewPort = true;
|
||||
action: pptrActions.VIEWPORT,
|
||||
})
|
||||
this._hasViewPort = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,97 +176,97 @@ class RecordingController {
|
||||
this.handleMessage({
|
||||
selector: undefined,
|
||||
value: undefined,
|
||||
action: pptrActions.NAVIGATION
|
||||
});
|
||||
action: pptrActions.NAVIGATION,
|
||||
})
|
||||
}
|
||||
|
||||
recordScreenshot(value) {
|
||||
this.handleMessage({
|
||||
selector: undefined,
|
||||
value,
|
||||
action: pptrActions.SCREENSHOT
|
||||
});
|
||||
action: pptrActions.SCREENSHOT,
|
||||
})
|
||||
}
|
||||
|
||||
handleMessage(msg, sender) {
|
||||
if (msg.control) return this.handleControlMessage(msg, sender);
|
||||
if (msg.control) return this.handleControlMessage(msg, sender)
|
||||
|
||||
if (msg.type === "SIGN_CONNECT") {
|
||||
return;
|
||||
if (msg.type === 'SIGN_CONNECT') {
|
||||
return
|
||||
}
|
||||
|
||||
// to account for clicks etc. we need to record the frameId and url to later target the frame in playback
|
||||
msg.frameId = sender ? sender.frameId : null;
|
||||
msg.frameUrl = sender ? sender.url : null;
|
||||
msg.frameId = sender ? sender.frameId : null
|
||||
msg.frameUrl = sender ? sender.url : null
|
||||
|
||||
if (!this._isPaused) {
|
||||
this._recording.push(msg);
|
||||
console.log(msg);
|
||||
this._recording.push(msg)
|
||||
console.log(msg)
|
||||
chrome.storage.local.set({ recording: this._recording }, () => {
|
||||
console.debug("stored recording updated");
|
||||
});
|
||||
console.debug('stored recording updated')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleControlMessage(msg) {
|
||||
if (msg.control === ctrl.EVENT_RECORDER_STARTED)
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState });
|
||||
chrome.browserAction.setBadgeText({ text: this._badgeState })
|
||||
if (msg.control === ctrl.GET_VIEWPORT_SIZE)
|
||||
this.recordCurrentViewportSize(msg.coordinates);
|
||||
if (msg.control === ctrl.GET_CURRENT_URL) this.recordCurrentUrl(msg.href);
|
||||
if (msg.control === ctrl.GET_SCREENSHOT) this.recordScreenshot(msg.value);
|
||||
this.recordCurrentViewportSize(msg.coordinates)
|
||||
if (msg.control === ctrl.GET_CURRENT_URL) this.recordCurrentUrl(msg.href)
|
||||
if (msg.control === ctrl.GET_SCREENSHOT) this.recordScreenshot(msg.value)
|
||||
}
|
||||
|
||||
handleNavigation({ frameId }) {
|
||||
console.debug("frameId is:", frameId);
|
||||
this.injectScript();
|
||||
console.debug('frameId is:', frameId)
|
||||
this.injectScript()
|
||||
if (frameId === 0) {
|
||||
this.recordNavigation();
|
||||
this.recordNavigation()
|
||||
}
|
||||
}
|
||||
|
||||
handleMenuInteraction(info) {
|
||||
console.debug("context menu clicked");
|
||||
console.debug('context menu clicked')
|
||||
switch (info.menuItemId) {
|
||||
case this._menuId + this._menuOptions.SCREENSHOT:
|
||||
this.toggleScreenShotMode(actions.TOGGLE_SCREENSHOT_MODE);
|
||||
break;
|
||||
this.toggleScreenShotMode(actions.TOGGLE_SCREENSHOT_MODE)
|
||||
break
|
||||
case this._menuId + this._menuOptions.SCREENSHOT_CLIPPED:
|
||||
this.toggleScreenShotMode(actions.TOGGLE_SCREENSHOT_CLIPPED_MODE);
|
||||
break;
|
||||
this.toggleScreenShotMode(actions.TOGGLE_SCREENSHOT_CLIPPED_MODE)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyCommands(command) {
|
||||
switch (command) {
|
||||
case actions.TOGGLE_SCREENSHOT_MODE:
|
||||
this.toggleScreenShotMode(actions.TOGGLE_SCREENSHOT_MODE);
|
||||
break;
|
||||
this.toggleScreenShotMode(actions.TOGGLE_SCREENSHOT_MODE)
|
||||
break
|
||||
case actions.TOGGLE_SCREENSHOT_CLIPPED_MODE:
|
||||
this.toggleScreenShotMode(actions.TOGGLE_SCREENSHOT_CLIPPED_MODE);
|
||||
break;
|
||||
this.toggleScreenShotMode(actions.TOGGLE_SCREENSHOT_CLIPPED_MODE)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
toggleScreenShotMode(action) {
|
||||
console.debug("toggling screenshot mode");
|
||||
console.debug('toggling screenshot mode')
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
|
||||
chrome.tabs.sendMessage(tabs[0].id, { action });
|
||||
});
|
||||
chrome.tabs.sendMessage(tabs[0].id, { action })
|
||||
})
|
||||
}
|
||||
|
||||
handleWait() {
|
||||
chrome.browserAction.setBadgeText({ text: "wait" });
|
||||
chrome.browserAction.setBadgeText({ text: 'wait' })
|
||||
}
|
||||
|
||||
injectScript() {
|
||||
chrome.tabs.executeScript({
|
||||
file: "js/content-script.js",
|
||||
allFrames: true
|
||||
});
|
||||
file: 'js/content-script.js',
|
||||
allFrames: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
console.debug("booting recording controller");
|
||||
window.recordingController = new RecordingController();
|
||||
window.recordingController.boot();
|
||||
console.debug('booting recording controller')
|
||||
window.recordingController = new RecordingController()
|
||||
window.recordingController.boot()
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ChecklyBadge"
|
||||
};
|
||||
name: 'ChecklyBadge',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "HelloWorld",
|
||||
name: 'HelloWorld',
|
||||
mounted() {
|
||||
browser.runtime.sendMessage({});
|
||||
browser.runtime.sendMessage({})
|
||||
},
|
||||
computed: {
|
||||
defaultText() {
|
||||
return browser.i18n.getMessage("extName");
|
||||
}
|
||||
}
|
||||
};
|
||||
return browser.i18n.getMessage('extName')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -62,14 +62,14 @@ node my-script.js</pre
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "HelpTab"
|
||||
};
|
||||
name: 'HelpTab',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/styles/_variables.scss";
|
||||
@import "../assets/styles/_mixins.scss";
|
||||
@import "../assets/styles/_utils.scss";
|
||||
@import '../assets/styles/_variables.scss';
|
||||
@import '../assets/styles/_mixins.scss';
|
||||
@import '../assets/styles/_utils.scss';
|
||||
|
||||
.help-tab {
|
||||
.content {
|
||||
|
||||
@@ -40,33 +40,33 @@
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: "RecordingTab",
|
||||
name: 'RecordingTab',
|
||||
props: {
|
||||
isRecording: { type: Boolean, default: false },
|
||||
liveEvents: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return []
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
parseEventValue(event) {
|
||||
if (!event) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (event.action === "viewport*")
|
||||
return `width: ${event.value.width}, height: ${event.value.height}`;
|
||||
if (event.action === "goto*") return event.href;
|
||||
if (event.action === "navigation*") return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
if (event.action === 'viewport*')
|
||||
return `width: ${event.value.width}, height: ${event.value.height}`
|
||||
if (event.action === 'goto*') return event.href
|
||||
if (event.action === 'navigation*') return ''
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/styles/_animations.scss";
|
||||
@import "../assets/styles/_variables.scss";
|
||||
@import '../assets/styles/_animations.scss';
|
||||
@import '../assets/styles/_variables.scss';
|
||||
|
||||
.recording-tab {
|
||||
.content {
|
||||
@@ -88,7 +88,7 @@ export default {
|
||||
flex-direction: column-reverse;
|
||||
|
||||
.loading:after {
|
||||
content: ".";
|
||||
content: '.';
|
||||
animation: dots 1s steps(5, end) infinite;
|
||||
animation-delay: 1.5s;
|
||||
margin-bottom: auto;
|
||||
|
||||
@@ -31,31 +31,31 @@
|
||||
</template>
|
||||
<script>
|
||||
export const TYPE = {
|
||||
PUPPETEER: "puppeteer",
|
||||
PLAYWRIGHT: "playwright"
|
||||
};
|
||||
PUPPETEER: 'puppeteer',
|
||||
PLAYWRIGHT: 'playwright',
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "ResultsTab",
|
||||
name: 'ResultsTab',
|
||||
props: {
|
||||
puppeteer: {
|
||||
type: String,
|
||||
default: ""
|
||||
default: '',
|
||||
},
|
||||
playwright: {
|
||||
type: String,
|
||||
default: ""
|
||||
default: '',
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: TYPE.PUPPETEER,
|
||||
tabs: [TYPE.PUPPETEER, TYPE.PLAYWRIGHT]
|
||||
};
|
||||
tabs: [TYPE.PUPPETEER, TYPE.PLAYWRIGHT],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (
|
||||
@@ -63,27 +63,27 @@ export default {
|
||||
this.options.code &&
|
||||
this.options.code.showPlaywrightFirst
|
||||
) {
|
||||
this.activeTab = TYPE.PLAYWRIGHT;
|
||||
this.tabs = this.tabs.reverse();
|
||||
this.activeTab = TYPE.PLAYWRIGHT
|
||||
this.tabs = this.tabs.reverse()
|
||||
}
|
||||
this.$emit("update:tab", this.activeTab);
|
||||
this.$emit('update:tab', this.activeTab)
|
||||
},
|
||||
methods: {
|
||||
code() {
|
||||
return this.activeTab === TYPE.PUPPETEER
|
||||
? this.puppeteer
|
||||
: this.playwright;
|
||||
: this.playwright
|
||||
},
|
||||
changeTab(tab) {
|
||||
this.activeTab = tab;
|
||||
this.$emit("update:tab", tab);
|
||||
}
|
||||
}
|
||||
};
|
||||
this.activeTab = tab
|
||||
this.$emit('update:tab', tab)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/styles/_variables.scss";
|
||||
@import '../assets/styles/_variables.scss';
|
||||
|
||||
.results-tab {
|
||||
.content {
|
||||
|
||||
@@ -1,47 +1,46 @@
|
||||
import eventsToRecord from "@/services/dom-events-to-record";
|
||||
import UIController from "./UIController";
|
||||
import actions from "@/models/extension-ui-actions";
|
||||
import ctrl from "@/models/extension-control-messages";
|
||||
import finder from "@medv/finder";
|
||||
import eventsToRecord from '@/services/dom-events-to-record'
|
||||
import UIController from './UIController'
|
||||
import actions from '@/models/extension-ui-actions'
|
||||
import ctrl from '@/models/extension-control-messages'
|
||||
import finder from '@medv/finder'
|
||||
|
||||
const DEFAULT_MOUSE_CURSOR = "default";
|
||||
const DEFAULT_MOUSE_CURSOR = 'default'
|
||||
|
||||
export default class EventRecorder {
|
||||
constructor() {
|
||||
this._boundedMessageListener = null;
|
||||
this._eventLog = [];
|
||||
this._previousEvent = null;
|
||||
this._dataAttribute = null;
|
||||
this._uiController = null;
|
||||
this._screenShotMode = false;
|
||||
this._isTopFrame = window.location === window.parent.location;
|
||||
this._isRecordingClicks = true;
|
||||
this._boundedMessageListener = null
|
||||
this._eventLog = []
|
||||
this._previousEvent = null
|
||||
this._dataAttribute = null
|
||||
this._uiController = null
|
||||
this._screenShotMode = false
|
||||
this._isTopFrame = window.location === window.parent.location
|
||||
this._isRecordingClicks = true
|
||||
}
|
||||
|
||||
boot() {
|
||||
// We need to check the existence of chrome for testing purposes
|
||||
if (chrome.storage && chrome.storage.local) {
|
||||
chrome.storage.local.get(["options"], ({ options }) => {
|
||||
const { dataAttribute } = options ? options.code : {};
|
||||
chrome.storage.local.get(['options'], ({ options }) => {
|
||||
const { dataAttribute } = options ? options.code : {}
|
||||
if (dataAttribute) {
|
||||
this._dataAttribute = dataAttribute;
|
||||
this._dataAttribute = dataAttribute
|
||||
}
|
||||
this._initializeRecorder();
|
||||
});
|
||||
this._initializeRecorder()
|
||||
})
|
||||
} else {
|
||||
this._initializeRecorder();
|
||||
this._initializeRecorder()
|
||||
}
|
||||
}
|
||||
|
||||
_initializeRecorder() {
|
||||
const events = Object.values(eventsToRecord);
|
||||
const events = Object.values(eventsToRecord)
|
||||
if (!window.pptRecorderAddedControlListeners) {
|
||||
this._addAllListeners(events);
|
||||
this._addAllListeners(events)
|
||||
this._boundedMessageListener =
|
||||
this._boundedMessageListener ||
|
||||
this._handleBackgroundMessage.bind(this);
|
||||
chrome.runtime.onMessage.addListener(this._boundedMessageListener);
|
||||
window.pptRecorderAddedControlListeners = true;
|
||||
this._boundedMessageListener || this._handleBackgroundMessage.bind(this)
|
||||
chrome.runtime.onMessage.addListener(this._boundedMessageListener)
|
||||
window.pptRecorderAddedControlListeners = true
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -49,66 +48,66 @@ export default class EventRecorder {
|
||||
chrome.runtime &&
|
||||
chrome.runtime.onMessage
|
||||
) {
|
||||
window.document.pptRecorderAddedControlListeners = true;
|
||||
window.document.pptRecorderAddedControlListeners = true
|
||||
}
|
||||
|
||||
if (this._isTopFrame) {
|
||||
this._sendMessage({ control: ctrl.EVENT_RECORDER_STARTED });
|
||||
this._sendMessage({ control: ctrl.EVENT_RECORDER_STARTED })
|
||||
this._sendMessage({
|
||||
control: ctrl.GET_CURRENT_URL,
|
||||
href: window.location.href
|
||||
});
|
||||
href: window.location.href,
|
||||
})
|
||||
this._sendMessage({
|
||||
control: ctrl.GET_VIEWPORT_SIZE,
|
||||
coordinates: { width: window.innerWidth, height: window.innerHeight }
|
||||
});
|
||||
console.debug("Puppeteer Recorder in-page EventRecorder started");
|
||||
coordinates: { width: window.innerWidth, height: window.innerHeight },
|
||||
})
|
||||
console.debug('Puppeteer Recorder in-page EventRecorder started')
|
||||
}
|
||||
}
|
||||
|
||||
_handleBackgroundMessage(msg) {
|
||||
console.debug("content-script: message from background", msg);
|
||||
console.debug('content-script: message from background', msg)
|
||||
if (msg && msg.action) {
|
||||
switch (msg.action) {
|
||||
case actions.TOGGLE_SCREENSHOT_MODE:
|
||||
this._handleScreenshotMode(false);
|
||||
break;
|
||||
this._handleScreenshotMode(false)
|
||||
break
|
||||
case actions.TOGGLE_SCREENSHOT_CLIPPED_MODE:
|
||||
this._handleScreenshotMode(true);
|
||||
break;
|
||||
this._handleScreenshotMode(true)
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_addAllListeners(events) {
|
||||
const boundedRecordEvent = this._recordEvent.bind(this);
|
||||
const boundedRecordEvent = this._recordEvent.bind(this)
|
||||
events.forEach(type => {
|
||||
window.addEventListener(type, boundedRecordEvent, true);
|
||||
});
|
||||
window.addEventListener(type, boundedRecordEvent, true)
|
||||
})
|
||||
}
|
||||
|
||||
_sendMessage(msg) {
|
||||
// filter messages based on enabled / disabled features
|
||||
if (msg.action === "click" && !this._isRecordingClicks) return;
|
||||
if (msg.action === 'click' && !this._isRecordingClicks) return
|
||||
|
||||
try {
|
||||
// poor man's way of detecting whether this script was injected by an actual extension, or is loaded for
|
||||
// testing purposes
|
||||
if (chrome.runtime && chrome.runtime.onMessage) {
|
||||
chrome.runtime.sendMessage(msg);
|
||||
chrome.runtime.sendMessage(msg)
|
||||
} else {
|
||||
this._eventLog.push(msg);
|
||||
this._eventLog.push(msg)
|
||||
}
|
||||
} catch (err) {
|
||||
console.debug("caught error", err);
|
||||
console.debug('caught error', err)
|
||||
}
|
||||
}
|
||||
|
||||
_recordEvent(e) {
|
||||
if (this._previousEvent && this._previousEvent.timeStamp === e.timeStamp)
|
||||
return;
|
||||
this._previousEvent = e;
|
||||
return
|
||||
this._previousEvent = e
|
||||
|
||||
// we explicitly catch any errors and swallow them, as none node-type events are also ingested.
|
||||
// for these events we cannot generate selectors, which is OK
|
||||
@@ -120,67 +119,67 @@ export default class EventRecorder {
|
||||
action: e.type,
|
||||
keyCode: e.keyCode ? e.keyCode : null,
|
||||
href: e.target.href ? e.target.href : null,
|
||||
coordinates: EventRecorder._getCoordinates(e)
|
||||
});
|
||||
coordinates: EventRecorder._getCoordinates(e),
|
||||
})
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
_getEventLog() {
|
||||
return this._eventLog;
|
||||
return this._eventLog
|
||||
}
|
||||
|
||||
_clearEventLog() {
|
||||
this._eventLog = [];
|
||||
this._eventLog = []
|
||||
}
|
||||
|
||||
_handleScreenshotMode(isClipped) {
|
||||
this._disableClickRecording();
|
||||
this._uiController = new UIController({ showSelector: isClipped });
|
||||
this._screenShotMode = !this._screenShotMode;
|
||||
document.body.style.cursor = "crosshair";
|
||||
this._disableClickRecording()
|
||||
this._uiController = new UIController({ showSelector: isClipped })
|
||||
this._screenShotMode = !this._screenShotMode
|
||||
document.body.style.cursor = 'crosshair'
|
||||
|
||||
console.debug("screenshot mode:", this._screenShotMode);
|
||||
console.debug('screenshot mode:', this._screenShotMode)
|
||||
|
||||
if (this._screenShotMode) {
|
||||
this._uiController.showSelector();
|
||||
this._uiController.showSelector()
|
||||
} else {
|
||||
this._uiController.hideSelector();
|
||||
this._uiController.hideSelector()
|
||||
}
|
||||
|
||||
this._uiController.on("click", event => {
|
||||
this._screenShotMode = false;
|
||||
document.body.style.cursor = DEFAULT_MOUSE_CURSOR;
|
||||
this._sendMessage({ control: ctrl.GET_SCREENSHOT, value: event.clip });
|
||||
this._enableClickRecording();
|
||||
});
|
||||
this._uiController.on('click', event => {
|
||||
this._screenShotMode = false
|
||||
document.body.style.cursor = DEFAULT_MOUSE_CURSOR
|
||||
this._sendMessage({ control: ctrl.GET_SCREENSHOT, value: event.clip })
|
||||
this._enableClickRecording()
|
||||
})
|
||||
}
|
||||
|
||||
_disableClickRecording() {
|
||||
this._isRecordingClicks = false;
|
||||
this._isRecordingClicks = false
|
||||
}
|
||||
|
||||
_enableClickRecording() {
|
||||
this._isRecordingClicks = true;
|
||||
this._isRecordingClicks = true
|
||||
}
|
||||
|
||||
_getSelector(e) {
|
||||
if (this._dataAttribute && e.target.getAttribute(this._dataAttribute)) {
|
||||
return `[${this._dataAttribute}="${e.target.getAttribute(
|
||||
this._dataAttribute
|
||||
)}"]`;
|
||||
)}"]`
|
||||
}
|
||||
|
||||
if (e.target.id) {
|
||||
return `#${e.target.id}`;
|
||||
return `#${e.target.id}`
|
||||
}
|
||||
|
||||
return finder(e.target, {
|
||||
seedMinLength: 5,
|
||||
optimizedMinLength: e.target.id ? 2 : 10,
|
||||
attr: name => name === this._dataAttribute
|
||||
});
|
||||
attr: name => name === this._dataAttribute,
|
||||
})
|
||||
}
|
||||
|
||||
static _getCoordinates(evt) {
|
||||
@@ -188,10 +187,10 @@ export default class EventRecorder {
|
||||
mouseup: true,
|
||||
mousedown: true,
|
||||
mousemove: true,
|
||||
mouseover: true
|
||||
};
|
||||
mouseover: true,
|
||||
}
|
||||
return eventsWithCoordinates[evt.type]
|
||||
? { x: evt.clientX, y: evt.clientY }
|
||||
: null;
|
||||
: null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +1,120 @@
|
||||
import EventEmitter from "events";
|
||||
import EventEmitter from 'events'
|
||||
|
||||
const BORDER_THICKNESS = 3;
|
||||
const BORDER_THICKNESS = 3
|
||||
|
||||
const defaults = {
|
||||
showSelector: false
|
||||
};
|
||||
showSelector: false,
|
||||
}
|
||||
|
||||
class UIController extends EventEmitter {
|
||||
constructor(options) {
|
||||
options = Object.assign({}, defaults, options);
|
||||
options = Object.assign({}, defaults, options)
|
||||
|
||||
super();
|
||||
this._overlay = null;
|
||||
this._selector = null;
|
||||
this._element = null;
|
||||
this._dimensions = {};
|
||||
this._showSelector = options.showSelector;
|
||||
super()
|
||||
this._overlay = null
|
||||
this._selector = null
|
||||
this._element = null
|
||||
this._dimensions = {}
|
||||
this._showSelector = options.showSelector
|
||||
|
||||
this._boundeMouseMove = this._mousemove.bind(this);
|
||||
this._boundeMouseUp = this._mouseup.bind(this);
|
||||
this._boundeMouseMove = this._mousemove.bind(this)
|
||||
this._boundeMouseUp = this._mouseup.bind(this)
|
||||
}
|
||||
|
||||
showSelector() {
|
||||
console.debug("UIController:show");
|
||||
console.debug('UIController:show')
|
||||
if (!this._overlay) {
|
||||
this._overlay = document.createElement("div");
|
||||
this._overlay.className = "headlessRecorderOverlay";
|
||||
this._overlay.style.position = "fixed";
|
||||
this._overlay.style.top = "0px";
|
||||
this._overlay.style.left = "0px";
|
||||
this._overlay.style.width = "100%";
|
||||
this._overlay.style.height = "100%";
|
||||
this._overlay.style.pointerEvents = "none";
|
||||
this._overlay = document.createElement('div')
|
||||
this._overlay.className = 'headlessRecorderOverlay'
|
||||
this._overlay.style.position = 'fixed'
|
||||
this._overlay.style.top = '0px'
|
||||
this._overlay.style.left = '0px'
|
||||
this._overlay.style.width = '100%'
|
||||
this._overlay.style.height = '100%'
|
||||
this._overlay.style.pointerEvents = 'none'
|
||||
|
||||
if (this._showSelector) {
|
||||
this._selector = document.createElement("div");
|
||||
this._selector.className = "headlessRecorderOutline";
|
||||
this._selector.style.position = "fixed";
|
||||
this._selector = document.createElement('div')
|
||||
this._selector.className = 'headlessRecorderOutline'
|
||||
this._selector.style.position = 'fixed'
|
||||
this._selector.style.border =
|
||||
BORDER_THICKNESS + "px solid rgba(69,200,241,0.8)";
|
||||
this._selector.style.borderRadius = "3px";
|
||||
this._overlay.appendChild(this._selector);
|
||||
BORDER_THICKNESS + 'px solid rgba(69,200,241,0.8)'
|
||||
this._selector.style.borderRadius = '3px'
|
||||
this._overlay.appendChild(this._selector)
|
||||
}
|
||||
}
|
||||
if (!this._overlay.parentNode) {
|
||||
document.body.appendChild(this._overlay);
|
||||
document.body.addEventListener("mousemove", this._boundeMouseMove, false);
|
||||
document.body.addEventListener("click", this._boundeMouseUp, false);
|
||||
document.body.appendChild(this._overlay)
|
||||
document.body.addEventListener('mousemove', this._boundeMouseMove, false)
|
||||
document.body.addEventListener('click', this._boundeMouseUp, false)
|
||||
}
|
||||
}
|
||||
|
||||
hideSelector() {
|
||||
console.debug("UIController:hide");
|
||||
console.debug('UIController:hide')
|
||||
if (this._overlay) {
|
||||
document.body.removeChild(this._overlay);
|
||||
document.body.removeChild(this._overlay)
|
||||
}
|
||||
this._overlay = this._selector = this._element = null;
|
||||
this._dimensions = {};
|
||||
this._overlay = this._selector = this._element = null
|
||||
this._dimensions = {}
|
||||
}
|
||||
|
||||
_mousemove(e) {
|
||||
if (this._element !== e.target) {
|
||||
this._element = e.target;
|
||||
this._element = e.target
|
||||
|
||||
this._dimensions.top = -window.scrollY;
|
||||
this._dimensions.left = -window.scrollX;
|
||||
this._dimensions.top = -window.scrollY
|
||||
this._dimensions.left = -window.scrollX
|
||||
|
||||
let elem = e.target;
|
||||
let elem = e.target
|
||||
|
||||
while (elem && elem !== document.body) {
|
||||
this._dimensions.top += elem.offsetTop;
|
||||
this._dimensions.left += elem.offsetLeft;
|
||||
elem = elem.offsetParent;
|
||||
this._dimensions.top += elem.offsetTop
|
||||
this._dimensions.left += elem.offsetLeft
|
||||
elem = elem.offsetParent
|
||||
}
|
||||
this._dimensions.width = this._element.offsetWidth;
|
||||
this._dimensions.height = this._element.offsetHeight;
|
||||
this._dimensions.width = this._element.offsetWidth
|
||||
this._dimensions.height = this._element.offsetHeight
|
||||
|
||||
if (this._selector) {
|
||||
this._selector.style.top =
|
||||
this._dimensions.top - BORDER_THICKNESS + "px";
|
||||
this._dimensions.top - BORDER_THICKNESS + 'px'
|
||||
this._selector.style.left =
|
||||
this._dimensions.left - BORDER_THICKNESS + "px";
|
||||
this._selector.style.width = this._dimensions.width + "px";
|
||||
this._selector.style.height = this._dimensions.height + "px";
|
||||
this._dimensions.left - BORDER_THICKNESS + 'px'
|
||||
this._selector.style.width = this._dimensions.width + 'px'
|
||||
this._selector.style.height = this._dimensions.height + 'px'
|
||||
console.debug(
|
||||
`top: ${this._selector.style.top}, left: ${this._selector.style.left}, width: ${this._selector.style.width}, height: ${this._selector.style.height}`
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
_mouseup(e) {
|
||||
this._overlay.style.backgroundColor = "white";
|
||||
this._overlay.style.backgroundColor = 'white'
|
||||
setTimeout(() => {
|
||||
this._overlay.style.backgroundColor = "none";
|
||||
this._cleanup();
|
||||
this._overlay.style.backgroundColor = 'none'
|
||||
this._cleanup()
|
||||
|
||||
let clip = null;
|
||||
let clip = null
|
||||
|
||||
if (this._selector) {
|
||||
clip = {
|
||||
x: this._selector.style.left,
|
||||
y: this._selector.style.top,
|
||||
width: this._selector.style.width,
|
||||
height: this._selector.style.height
|
||||
};
|
||||
height: this._selector.style.height,
|
||||
}
|
||||
}
|
||||
|
||||
this.emit("click", { clip, raw: e });
|
||||
}, 100);
|
||||
this.emit('click', { clip, raw: e })
|
||||
}, 100)
|
||||
}
|
||||
|
||||
_cleanup() {
|
||||
document.body.removeEventListener(
|
||||
"mousemove",
|
||||
this._boundeMouseMove,
|
||||
false
|
||||
);
|
||||
document.body.removeEventListener("mouseup", this._boundeMouseUp, false);
|
||||
document.body.removeChild(this._overlay);
|
||||
document.body.removeEventListener('mousemove', this._boundeMouseMove, false)
|
||||
document.body.removeEventListener('mouseup', this._boundeMouseUp, false)
|
||||
document.body.removeChild(this._overlay)
|
||||
}
|
||||
}
|
||||
|
||||
export default UIController;
|
||||
export default UIController
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
import UIController from "../UIController";
|
||||
import UIController from '../UIController'
|
||||
|
||||
// this test NEEDS to come first because of shitty JSDOM.
|
||||
// See https://github.com/facebook/jest/issues/1224
|
||||
it("Registers mouse events", () => {
|
||||
jest.useFakeTimers();
|
||||
it('Registers mouse events', () => {
|
||||
jest.useFakeTimers()
|
||||
|
||||
document.body.innerHTML =
|
||||
"<div>" +
|
||||
'<div>' +
|
||||
' <div id="username">UserName</div>' +
|
||||
' <button id="button"></button>' +
|
||||
"</div>";
|
||||
'</div>'
|
||||
|
||||
const uic = new UIController();
|
||||
uic.showSelector();
|
||||
const uic = new UIController()
|
||||
uic.showSelector()
|
||||
|
||||
const handleClick = jest.fn();
|
||||
uic.on("click", handleClick);
|
||||
const handleClick = jest.fn()
|
||||
uic.on('click', handleClick)
|
||||
|
||||
const el = document.querySelector("#username");
|
||||
el.click();
|
||||
const el = document.querySelector('#username')
|
||||
el.click()
|
||||
|
||||
jest.runAllTimers();
|
||||
jest.runAllTimers()
|
||||
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
expect(handleClick).toHaveBeenCalled();
|
||||
});
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1)
|
||||
expect(handleClick).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("Shows and hides the selector", () => {
|
||||
const uic = new UIController();
|
||||
it('Shows and hides the selector', () => {
|
||||
const uic = new UIController()
|
||||
|
||||
uic.showSelector();
|
||||
let overlay = document.querySelector(".headlessRecorderOverlay");
|
||||
let outline = document.querySelector(".headlessRecorderOutline");
|
||||
uic.showSelector()
|
||||
let overlay = document.querySelector('.headlessRecorderOverlay')
|
||||
let outline = document.querySelector('.headlessRecorderOutline')
|
||||
|
||||
expect(overlay).toBeDefined();
|
||||
expect(outline).toBeDefined();
|
||||
expect(overlay).toBeDefined()
|
||||
expect(outline).toBeDefined()
|
||||
|
||||
uic.hideSelector();
|
||||
overlay = document.querySelector(".headlessRecorderOverlay");
|
||||
outline = document.querySelector(".headlessRecorderOutline");
|
||||
uic.hideSelector()
|
||||
overlay = document.querySelector('.headlessRecorderOverlay')
|
||||
outline = document.querySelector('.headlessRecorderOutline')
|
||||
|
||||
expect(overlay).toBeNull();
|
||||
expect(outline).toBeNull();
|
||||
});
|
||||
expect(overlay).toBeNull()
|
||||
expect(outline).toBeNull()
|
||||
})
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
import express from "express";
|
||||
import path from "path";
|
||||
import express from 'express'
|
||||
import path from 'path'
|
||||
|
||||
export const waitForAndGetEvents = async function(page, amount) {
|
||||
await waitForRecorderEvents(page, amount);
|
||||
return getEventLog(page);
|
||||
};
|
||||
await waitForRecorderEvents(page, amount)
|
||||
return getEventLog(page)
|
||||
}
|
||||
|
||||
export const waitForRecorderEvents = function(page, amount) {
|
||||
return page.waitForFunction(
|
||||
`window.eventRecorder._getEventLog().length >= ${amount || 1}`
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export const getEventLog = function(page) {
|
||||
return page.evaluate(() => {
|
||||
return window.eventRecorder._getEventLog();
|
||||
});
|
||||
};
|
||||
return window.eventRecorder._getEventLog()
|
||||
})
|
||||
}
|
||||
|
||||
export const cleanEventLog = function(page) {
|
||||
return page.evaluate(() => {
|
||||
return window.eventRecorder._clearEventLog();
|
||||
});
|
||||
};
|
||||
return window.eventRecorder._clearEventLog()
|
||||
})
|
||||
}
|
||||
|
||||
export const startServer = function(buildDir, file) {
|
||||
return new Promise(resolve => {
|
||||
const app = express();
|
||||
app.use("/build", express.static(path.join(__dirname, buildDir)));
|
||||
app.get("/", (req, res) => {
|
||||
res.status(200).sendFile(file, { root: __dirname });
|
||||
});
|
||||
let server;
|
||||
let port;
|
||||
const app = express()
|
||||
app.use('/build', express.static(path.join(__dirname, buildDir)))
|
||||
app.get('/', (req, res) => {
|
||||
res.status(200).sendFile(file, { root: __dirname })
|
||||
})
|
||||
let server
|
||||
let port
|
||||
const retry = e => {
|
||||
if (e.code === "EADDRINUSE") {
|
||||
setTimeout(() => connect, 1000);
|
||||
if (e.code === 'EADDRINUSE') {
|
||||
setTimeout(() => connect, 1000)
|
||||
}
|
||||
};
|
||||
}
|
||||
const connect = () => {
|
||||
port = 0 | (Math.random() * 1000 + 3000);
|
||||
server = app.listen(port);
|
||||
server.once("error", retry);
|
||||
server.once("listening", () => {
|
||||
return resolve({ server, port });
|
||||
});
|
||||
};
|
||||
connect();
|
||||
});
|
||||
};
|
||||
port = 0 | (Math.random() * 1000 + 3000)
|
||||
server = app.listen(port)
|
||||
server.once('error', retry)
|
||||
server.once('listening', () => {
|
||||
return resolve({ server, port })
|
||||
})
|
||||
}
|
||||
connect()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export default {
|
||||
EVENT_RECORDER_STARTED: "EVENT_RECORDER_STARTED",
|
||||
GET_VIEWPORT_SIZE: "GET_VIEWPORT_SIZE",
|
||||
GET_CURRENT_URL: "GET_CURRENT_URL",
|
||||
GET_SCREENSHOT: "GET_SCREENSHOT"
|
||||
};
|
||||
EVENT_RECORDER_STARTED: 'EVENT_RECORDER_STARTED',
|
||||
GET_VIEWPORT_SIZE: 'GET_VIEWPORT_SIZE',
|
||||
GET_CURRENT_URL: 'GET_CURRENT_URL',
|
||||
GET_SCREENSHOT: 'GET_SCREENSHOT',
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export default {
|
||||
TOGGLE_SCREENSHOT_MODE: "TOGGLE_SCREENSHOT_MODE",
|
||||
TOGGLE_SCREENSHOT_CLIPPED_MODE: "TOGGLE_SCREENSHOT_CLIPPED_MODE",
|
||||
START: "START",
|
||||
STOP: "STOP",
|
||||
CLEAN_UP: "CLEAN_UP",
|
||||
PAUSE: "PAUSE",
|
||||
UN_PAUSE: "UN_PAUSE"
|
||||
};
|
||||
TOGGLE_SCREENSHOT_MODE: 'TOGGLE_SCREENSHOT_MODE',
|
||||
TOGGLE_SCREENSHOT_CLIPPED_MODE: 'TOGGLE_SCREENSHOT_CLIPPED_MODE',
|
||||
START: 'START',
|
||||
STOP: 'STOP',
|
||||
CLEAN_UP: 'CLEAN_UP',
|
||||
PAUSE: 'PAUSE',
|
||||
UN_PAUSE: 'UN_PAUSE',
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@
|
||||
>
|
||||
{{
|
||||
recordingKeyCodePress
|
||||
? "Capturing"
|
||||
: "Click to capture key code"
|
||||
? 'Capturing'
|
||||
: 'Click to capture key code'
|
||||
}}
|
||||
</button>
|
||||
<input
|
||||
@@ -171,68 +171,68 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defaults as code } from "@/services/CodeGenerator";
|
||||
import { defaults as code } from '@/services/CodeGenerator'
|
||||
|
||||
const defaults = {
|
||||
code,
|
||||
extension: {
|
||||
telemetry: true
|
||||
}
|
||||
};
|
||||
telemetry: true,
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
name: 'App',
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
saving: false,
|
||||
options: defaults,
|
||||
recordingKeyCodePress: false
|
||||
};
|
||||
recordingKeyCodePress: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.load();
|
||||
this.load()
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
this.saving = true;
|
||||
this.saving = true
|
||||
chrome.storage.local.set({ options: this.options }, () => {
|
||||
console.debug("saved options");
|
||||
console.debug('saved options')
|
||||
setTimeout(() => {
|
||||
this.saving = false;
|
||||
}, 500);
|
||||
});
|
||||
this.saving = false
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
load() {
|
||||
chrome.storage.local.get("options", ({ options }) => {
|
||||
chrome.storage.local.get('options', ({ options }) => {
|
||||
if (options) {
|
||||
console.debug("loaded options", JSON.stringify(options));
|
||||
this.options = options;
|
||||
console.debug('loaded options', JSON.stringify(options))
|
||||
this.options = options
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
listenForKeyCodePress() {
|
||||
this.recordingKeyCodePress = true;
|
||||
this.recordingKeyCodePress = true
|
||||
const keyDownFunction = e => {
|
||||
this.recordingKeyCodePress = false;
|
||||
this.updateKeyCodeWithNumber(e);
|
||||
window.removeEventListener("keydown", keyDownFunction, false);
|
||||
e.preventDefault();
|
||||
};
|
||||
window.addEventListener("keydown", keyDownFunction, false);
|
||||
this.recordingKeyCodePress = false
|
||||
this.updateKeyCodeWithNumber(e)
|
||||
window.removeEventListener('keydown', keyDownFunction, false)
|
||||
e.preventDefault()
|
||||
}
|
||||
window.addEventListener('keydown', keyDownFunction, false)
|
||||
},
|
||||
updateKeyCodeWithNumber(evt) {
|
||||
this.options.code.keyCode = parseInt(evt.keyCode, 10);
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
};
|
||||
this.options.code.keyCode = parseInt(evt.keyCode, 10)
|
||||
this.save()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/styles/_variables.scss";
|
||||
@import "../assets/styles/_mixins.scss";
|
||||
@import '../assets/styles/_variables.scss';
|
||||
@import '../assets/styles/_mixins.scss';
|
||||
|
||||
.options {
|
||||
height: 100%;
|
||||
@@ -308,8 +308,8 @@ export default {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
input[type="text"],
|
||||
input[type="number"] {
|
||||
input[type='text'],
|
||||
input[type='number'] {
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
border: 1px solid $gray-light;
|
||||
@@ -319,7 +319,7 @@ export default {
|
||||
border-radius: 10px;
|
||||
-webkit-box-sizing: border-box;
|
||||
}
|
||||
input[type="number"] {
|
||||
input[type='number'] {
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
createApp(App).mount("#app");
|
||||
createApp(App).mount('#app')
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
{{ recordingBadgeText }}
|
||||
</div>
|
||||
<button @click="toggleShowHelp" class="header-button">
|
||||
<img src="@/assets/images/help.svg" alt="help"/>
|
||||
<img src="@/assets/images/help.svg" alt="help" />
|
||||
</button>
|
||||
<button @click="openOptions" class="header-button">
|
||||
<img src="@/assets/images/settings.svg" alt="settings"/>
|
||||
<img src="@/assets/images/settings.svg" alt="settings" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,26 +68,26 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { version } from "../../package.json";
|
||||
import { version } from '../../package.json'
|
||||
|
||||
import PuppeteerCodeGenerator from "@/services/PuppeteerCodeGenerator";
|
||||
import PlaywrightCodeGenerator from "@/services/PlaywrightCodeGenerator";
|
||||
import RecordingTab from "@/components/RecordingTab.vue";
|
||||
import ResultsTab from "@/components/ResultsTab.vue";
|
||||
import HelpTab from "@/components/HelpTab.vue";
|
||||
import ChecklyBadge from "@/components/ChecklyBadge.vue";
|
||||
import PuppeteerCodeGenerator from '@/services/PuppeteerCodeGenerator'
|
||||
import PlaywrightCodeGenerator from '@/services/PlaywrightCodeGenerator'
|
||||
import RecordingTab from '@/components/RecordingTab.vue'
|
||||
import ResultsTab from '@/components/ResultsTab.vue'
|
||||
import HelpTab from '@/components/HelpTab.vue'
|
||||
import ChecklyBadge from '@/components/ChecklyBadge.vue'
|
||||
|
||||
import actions from "@/models/extension-ui-actions";
|
||||
import actions from '@/models/extension-ui-actions'
|
||||
|
||||
let bus;
|
||||
let bus
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
name: 'App',
|
||||
components: { ResultsTab, RecordingTab, HelpTab, ChecklyBadge },
|
||||
data() {
|
||||
return {
|
||||
code: "",
|
||||
codeForPlaywright: "",
|
||||
code: '',
|
||||
codeForPlaywright: '',
|
||||
options: {},
|
||||
showResultsTab: false,
|
||||
showHelp: false,
|
||||
@@ -98,116 +98,116 @@ export default {
|
||||
isCopying: false,
|
||||
bus: null,
|
||||
version,
|
||||
currentResultTab: null
|
||||
};
|
||||
currentResultTab: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadState(() => {
|
||||
this.trackPageView();
|
||||
this.trackPageView()
|
||||
if (this.isRecording) {
|
||||
console.debug("opened in recording state, fetching recording events");
|
||||
chrome.storage.local.get(["recording", "options"], ({ recording }) => {
|
||||
console.debug("loaded recording", recording);
|
||||
this.liveEvents = recording;
|
||||
});
|
||||
console.debug('opened in recording state, fetching recording events')
|
||||
chrome.storage.local.get(['recording', 'options'], ({ recording }) => {
|
||||
console.debug('loaded recording', recording)
|
||||
this.liveEvents = recording
|
||||
})
|
||||
}
|
||||
|
||||
if (!this.isRecording && this.code) {
|
||||
this.showResultsTab = true;
|
||||
this.showResultsTab = true
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
bus = chrome.extension.connect({ name: "recordControls" });
|
||||
bus = chrome.extension.connect({ name: 'recordControls' })
|
||||
},
|
||||
methods: {
|
||||
toggleRecord() {
|
||||
if (this.isRecording) {
|
||||
this.stop();
|
||||
this.stop()
|
||||
} else {
|
||||
this.start();
|
||||
this.start()
|
||||
}
|
||||
this.isRecording = !this.isRecording;
|
||||
this.storeState();
|
||||
this.isRecording = !this.isRecording
|
||||
this.storeState()
|
||||
},
|
||||
togglePause() {
|
||||
if (this.isPaused) {
|
||||
bus.postMessage({ action: actions.UN_PAUSE });
|
||||
this.isPaused = false;
|
||||
bus.postMessage({ action: actions.UN_PAUSE })
|
||||
this.isPaused = false
|
||||
} else {
|
||||
bus.postMessage({ action: actions.PAUSE });
|
||||
this.isPaused = true;
|
||||
bus.postMessage({ action: actions.PAUSE })
|
||||
this.isPaused = true
|
||||
}
|
||||
this.storeState();
|
||||
this.storeState()
|
||||
},
|
||||
start() {
|
||||
this.trackEvent("Start");
|
||||
this.cleanUp();
|
||||
console.debug("start recorder");
|
||||
bus.postMessage({ action: actions.START });
|
||||
this.trackEvent('Start')
|
||||
this.cleanUp()
|
||||
console.debug('start recorder')
|
||||
bus.postMessage({ action: actions.START })
|
||||
},
|
||||
stop() {
|
||||
this.trackEvent("Stop");
|
||||
console.debug("stop recorder");
|
||||
bus.postMessage({ action: actions.STOP });
|
||||
this.trackEvent('Stop')
|
||||
console.debug('stop recorder')
|
||||
bus.postMessage({ action: actions.STOP })
|
||||
|
||||
chrome.storage.local.get(
|
||||
["recording", "options"],
|
||||
['recording', 'options'],
|
||||
({ recording, options }) => {
|
||||
console.debug("loaded recording", recording);
|
||||
console.debug("loaded options", options);
|
||||
console.debug('loaded recording', recording)
|
||||
console.debug('loaded options', options)
|
||||
|
||||
this.recording = recording;
|
||||
const codeOptions = options ? options.code : {};
|
||||
this.recording = recording
|
||||
const codeOptions = options ? options.code : {}
|
||||
|
||||
const codeGen = new PuppeteerCodeGenerator(codeOptions);
|
||||
const codeGenPlaywright = new PlaywrightCodeGenerator(codeOptions);
|
||||
this.code = codeGen.generate(this.recording);
|
||||
this.codeForPlaywright = codeGenPlaywright.generate(this.recording);
|
||||
this.showResultsTab = true;
|
||||
this.storeState();
|
||||
const codeGen = new PuppeteerCodeGenerator(codeOptions)
|
||||
const codeGenPlaywright = new PlaywrightCodeGenerator(codeOptions)
|
||||
this.code = codeGen.generate(this.recording)
|
||||
this.codeForPlaywright = codeGenPlaywright.generate(this.recording)
|
||||
this.showResultsTab = true
|
||||
this.storeState()
|
||||
}
|
||||
);
|
||||
)
|
||||
},
|
||||
restart() {
|
||||
this.cleanUp();
|
||||
bus.postMessage({ action: actions.CLEAN_UP });
|
||||
this.cleanUp()
|
||||
bus.postMessage({ action: actions.CLEAN_UP })
|
||||
},
|
||||
cleanUp() {
|
||||
this.recording = this.liveEvents = [];
|
||||
this.code = "";
|
||||
this.codeForPlaywright = "";
|
||||
this.showResultsTab = this.isRecording = this.isPaused = false;
|
||||
this.storeState();
|
||||
this.recording = this.liveEvents = []
|
||||
this.code = ''
|
||||
this.codeForPlaywright = ''
|
||||
this.showResultsTab = this.isRecording = this.isPaused = false
|
||||
this.storeState()
|
||||
},
|
||||
openOptions() {
|
||||
this.trackEvent("Options");
|
||||
this.trackEvent('Options')
|
||||
if (chrome.runtime.openOptionsPage) {
|
||||
chrome.runtime.openOptionsPage();
|
||||
chrome.runtime.openOptionsPage()
|
||||
}
|
||||
},
|
||||
loadState(cb) {
|
||||
chrome.storage.local.get(
|
||||
["controls", "code", "options", "codeForPlaywright"],
|
||||
['controls', 'code', 'options', 'codeForPlaywright'],
|
||||
({ controls, code, options, codeForPlaywright }) => {
|
||||
if (controls) {
|
||||
this.isRecording = controls.isRecording;
|
||||
this.isPaused = controls.isPaused;
|
||||
this.isRecording = controls.isRecording
|
||||
this.isPaused = controls.isPaused
|
||||
}
|
||||
|
||||
if (code) {
|
||||
this.code = code;
|
||||
this.code = code
|
||||
}
|
||||
|
||||
if (codeForPlaywright) {
|
||||
this.codeForPlaywright = codeForPlaywright;
|
||||
this.codeForPlaywright = codeForPlaywright
|
||||
}
|
||||
|
||||
if (options) {
|
||||
this.options = options;
|
||||
this.options = options
|
||||
}
|
||||
cb();
|
||||
cb()
|
||||
}
|
||||
);
|
||||
)
|
||||
},
|
||||
storeState() {
|
||||
chrome.storage.local.set({
|
||||
@@ -215,24 +215,24 @@ export default {
|
||||
codeForPlaywright: this.codeForPlaywright,
|
||||
controls: {
|
||||
isRecording: this.isRecording,
|
||||
isPaused: this.isPaused
|
||||
}
|
||||
});
|
||||
isPaused: this.isPaused,
|
||||
},
|
||||
})
|
||||
},
|
||||
setCopying() {
|
||||
this.trackEvent("Copy");
|
||||
this.isCopying = true;
|
||||
this.trackEvent('Copy')
|
||||
this.isCopying = true
|
||||
setTimeout(() => {
|
||||
this.isCopying = false;
|
||||
}, 1500);
|
||||
this.isCopying = false
|
||||
}, 1500)
|
||||
},
|
||||
goHome() {
|
||||
this.showResultsTab = false;
|
||||
this.showHelp = false;
|
||||
this.showResultsTab = false
|
||||
this.showHelp = false
|
||||
},
|
||||
toggleShowHelp() {
|
||||
this.trackEvent("Help");
|
||||
this.showHelp = !this.showHelp;
|
||||
this.trackEvent('Help')
|
||||
this.showHelp = !this.showHelp
|
||||
},
|
||||
trackEvent(event) {
|
||||
if (
|
||||
@@ -240,7 +240,7 @@ export default {
|
||||
this.options.extension &&
|
||||
this.options.extension.telemetry
|
||||
) {
|
||||
if (window._gaq) window._gaq.push(["_trackEvent", event, "clicked"]);
|
||||
if (window._gaq) window._gaq.push(['_trackEvent', event, 'clicked'])
|
||||
}
|
||||
},
|
||||
trackPageView() {
|
||||
@@ -249,36 +249,36 @@ export default {
|
||||
this.options.extension &&
|
||||
this.options.extension.telemetry
|
||||
) {
|
||||
if (window._gaq) window._gaq.push(["_trackPageview"]);
|
||||
if (window._gaq) window._gaq.push(['_trackPageview'])
|
||||
}
|
||||
},
|
||||
getCodeForCopy() {
|
||||
return this.currentResultTab === "puppeteer"
|
||||
return this.currentResultTab === 'puppeteer'
|
||||
? this.code
|
||||
: this.codeForPlaywright;
|
||||
}
|
||||
: this.codeForPlaywright
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
recordingBadgeText() {
|
||||
return this.isPaused ? "paused" : "recording";
|
||||
return this.isPaused ? 'paused' : 'recording'
|
||||
},
|
||||
recordButtonText() {
|
||||
return this.isRecording ? "Stop" : "Record";
|
||||
return this.isRecording ? 'Stop' : 'Record'
|
||||
},
|
||||
pauseButtonText() {
|
||||
return this.isPaused ? "Resume" : "Pause";
|
||||
return this.isPaused ? 'Resume' : 'Pause'
|
||||
},
|
||||
copyLinkText() {
|
||||
return this.isCopying ? "copied!" : "copy to clipboard";
|
||||
}
|
||||
}
|
||||
};
|
||||
return this.isCopying ? 'copied!' : 'copy to clipboard'
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/styles/_animations.scss";
|
||||
@import "../assets/styles/_variables.scss";
|
||||
@import "../assets/styles/_mixins.scss";
|
||||
@import '../assets/styles/_animations.scss';
|
||||
@import '../assets/styles/_variables.scss';
|
||||
@import '../assets/styles/_mixins.scss';
|
||||
.recorder {
|
||||
font-size: 14px;
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import VueHighlightJS from "vue3-highlightjs";
|
||||
import "highlight.js/styles/a11y-dark.css";
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import VueHighlightJS from 'vue3-highlightjs'
|
||||
import 'highlight.js/styles/a11y-dark.css'
|
||||
|
||||
import "@/assets/styles/main.scss";
|
||||
import '@/assets/styles/main.scss'
|
||||
|
||||
createApp(App)
|
||||
.use(VueHighlightJS)
|
||||
.mount("#app");
|
||||
.mount('#app')
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
export default class Block {
|
||||
constructor(frameId, line) {
|
||||
this._lines = [];
|
||||
this._frameId = frameId;
|
||||
this._lines = []
|
||||
this._frameId = frameId
|
||||
|
||||
if (line) {
|
||||
line.frameId = this._frameId;
|
||||
this._lines.push(line);
|
||||
line.frameId = this._frameId
|
||||
this._lines.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
addLineToTop(line) {
|
||||
line.frameId = this._frameId;
|
||||
this._lines.unshift(line);
|
||||
line.frameId = this._frameId
|
||||
this._lines.unshift(line)
|
||||
}
|
||||
|
||||
addLine(line) {
|
||||
line.frameId = this._frameId;
|
||||
this._lines.push(line);
|
||||
line.frameId = this._frameId
|
||||
this._lines.push(line)
|
||||
}
|
||||
|
||||
getLines() {
|
||||
return this._lines;
|
||||
return this._lines
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import domEvents from "./dom-events-to-record";
|
||||
import Block from "./Block";
|
||||
import pptrActions from "./pptr-actions";
|
||||
import domEvents from './dom-events-to-record'
|
||||
import Block from './Block'
|
||||
import pptrActions from './pptr-actions'
|
||||
|
||||
export const defaults = {
|
||||
wrapAsync: true,
|
||||
@@ -8,45 +8,45 @@ export const defaults = {
|
||||
waitForNavigation: true,
|
||||
waitForSelectorOnClick: true,
|
||||
blankLinesBetweenBlocks: true,
|
||||
dataAttribute: "",
|
||||
dataAttribute: '',
|
||||
showPlaywrightFirst: false,
|
||||
keyCode: 9
|
||||
};
|
||||
keyCode: 9,
|
||||
}
|
||||
|
||||
export default class CodeGenerator {
|
||||
constructor(options) {
|
||||
this._options = Object.assign(defaults, options);
|
||||
this._blocks = [];
|
||||
this._frame = "page";
|
||||
this._frameId = 0;
|
||||
this._allFrames = {};
|
||||
this._screenshotCounter = 1;
|
||||
this._options = Object.assign(defaults, options)
|
||||
this._blocks = []
|
||||
this._frame = 'page'
|
||||
this._frameId = 0
|
||||
this._allFrames = {}
|
||||
this._screenshotCounter = 1
|
||||
|
||||
this._hasNavigation = false;
|
||||
this._hasNavigation = false
|
||||
}
|
||||
|
||||
generate() {
|
||||
throw new Error("Not implemented.");
|
||||
throw new Error('Not implemented.')
|
||||
}
|
||||
|
||||
_getHeader() {
|
||||
console.debug(this._options);
|
||||
let hdr = this._options.wrapAsync ? this._wrappedHeader : this._header;
|
||||
console.debug(this._options)
|
||||
let hdr = this._options.wrapAsync ? this._wrappedHeader : this._header
|
||||
hdr = this._options.headless
|
||||
? hdr
|
||||
: hdr.replace("launch()", "launch({ headless: false })");
|
||||
return hdr;
|
||||
: hdr.replace('launch()', 'launch({ headless: false })')
|
||||
return hdr
|
||||
}
|
||||
|
||||
_getFooter() {
|
||||
return this._options.wrapAsync ? this._wrappedFooter : this._footer;
|
||||
return this._options.wrapAsync ? this._wrappedFooter : this._footer
|
||||
}
|
||||
|
||||
_parseEvents(events) {
|
||||
console.debug(`generating code for ${events ? events.length : 0} events`);
|
||||
let result = "";
|
||||
console.debug(`generating code for ${events ? events.length : 0} events`)
|
||||
let result = ''
|
||||
|
||||
if (!events) return result;
|
||||
if (!events) return result
|
||||
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
const {
|
||||
@@ -57,178 +57,178 @@ export default class CodeGenerator {
|
||||
keyCode,
|
||||
tagName,
|
||||
frameId,
|
||||
frameUrl
|
||||
} = events[i];
|
||||
frameUrl,
|
||||
} = events[i]
|
||||
const escapedSelector = selector
|
||||
? selector.replace(/\\/g, "\\\\")
|
||||
: selector;
|
||||
? selector.replace(/\\/g, '\\\\')
|
||||
: selector
|
||||
|
||||
// we need to keep a handle on what frames events originate from
|
||||
this._setFrames(frameId, frameUrl);
|
||||
this._setFrames(frameId, frameUrl)
|
||||
|
||||
switch (action) {
|
||||
case "keydown":
|
||||
case 'keydown':
|
||||
if (keyCode === this._options.keyCode) {
|
||||
this._blocks.push(
|
||||
this._handleKeyDown(escapedSelector, value, keyCode)
|
||||
);
|
||||
)
|
||||
}
|
||||
break;
|
||||
case "click":
|
||||
this._blocks.push(this._handleClick(escapedSelector, events));
|
||||
break;
|
||||
case "change":
|
||||
if (tagName === "SELECT") {
|
||||
this._blocks.push(this._handleChange(escapedSelector, value));
|
||||
break
|
||||
case 'click':
|
||||
this._blocks.push(this._handleClick(escapedSelector, events))
|
||||
break
|
||||
case 'change':
|
||||
if (tagName === 'SELECT') {
|
||||
this._blocks.push(this._handleChange(escapedSelector, value))
|
||||
}
|
||||
break;
|
||||
break
|
||||
case pptrActions.GOTO:
|
||||
this._blocks.push(this._handleGoto(href, frameId));
|
||||
break;
|
||||
this._blocks.push(this._handleGoto(href, frameId))
|
||||
break
|
||||
case pptrActions.VIEWPORT:
|
||||
this._blocks.push(this._handleViewport(value.width, value.height));
|
||||
break;
|
||||
this._blocks.push(this._handleViewport(value.width, value.height))
|
||||
break
|
||||
case pptrActions.NAVIGATION:
|
||||
this._blocks.push(this._handleWaitForNavigation());
|
||||
this._hasNavigation = true;
|
||||
break;
|
||||
this._blocks.push(this._handleWaitForNavigation())
|
||||
this._hasNavigation = true
|
||||
break
|
||||
case pptrActions.SCREENSHOT:
|
||||
this._blocks.push(this._handleScreenshot(value));
|
||||
break;
|
||||
this._blocks.push(this._handleScreenshot(value))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (this._hasNavigation && this._options.waitForNavigation) {
|
||||
console.debug("Adding navigationPromise declaration");
|
||||
console.debug('Adding navigationPromise declaration')
|
||||
const block = new Block(this._frameId, {
|
||||
type: pptrActions.NAVIGATION_PROMISE,
|
||||
value: "const navigationPromise = page.waitForNavigation()"
|
||||
});
|
||||
this._blocks.unshift(block);
|
||||
value: 'const navigationPromise = page.waitForNavigation()',
|
||||
})
|
||||
this._blocks.unshift(block)
|
||||
}
|
||||
|
||||
console.debug("post processing blocks:", this._blocks);
|
||||
this._postProcess();
|
||||
console.debug('post processing blocks:', this._blocks)
|
||||
this._postProcess()
|
||||
|
||||
const indent = this._options.wrapAsync ? " " : "";
|
||||
const newLine = `\n`;
|
||||
const indent = this._options.wrapAsync ? ' ' : ''
|
||||
const newLine = `\n`
|
||||
|
||||
for (let block of this._blocks) {
|
||||
const lines = block.getLines();
|
||||
const lines = block.getLines()
|
||||
for (let line of lines) {
|
||||
result += indent + line.value + newLine;
|
||||
result += indent + line.value + newLine
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
_setFrames(frameId, frameUrl) {
|
||||
if (frameId && frameId !== 0) {
|
||||
this._frameId = frameId;
|
||||
this._frame = `frame_${frameId}`;
|
||||
this._allFrames[frameId] = frameUrl;
|
||||
this._frameId = frameId
|
||||
this._frame = `frame_${frameId}`
|
||||
this._allFrames[frameId] = frameUrl
|
||||
} else {
|
||||
this._frameId = 0;
|
||||
this._frame = "page";
|
||||
this._frameId = 0
|
||||
this._frame = 'page'
|
||||
}
|
||||
}
|
||||
|
||||
_postProcess() {
|
||||
// when events are recorded from different frames, we want to add a frame setter near the code that uses that frame
|
||||
if (Object.keys(this._allFrames).length > 0) {
|
||||
this._postProcessSetFrames();
|
||||
this._postProcessSetFrames()
|
||||
}
|
||||
|
||||
if (this._options.blankLinesBetweenBlocks && this._blocks.length > 0) {
|
||||
this._postProcessAddBlankLines();
|
||||
this._postProcessAddBlankLines()
|
||||
}
|
||||
}
|
||||
|
||||
_handleKeyDown(selector, value) {
|
||||
const block = new Block(this._frameId);
|
||||
const block = new Block(this._frameId)
|
||||
block.addLine({
|
||||
type: domEvents.KEYDOWN,
|
||||
value: `await ${this._frame}.type('${selector}', '${this._escapeUserInput(
|
||||
value
|
||||
)}')`
|
||||
});
|
||||
return block;
|
||||
)}')`,
|
||||
})
|
||||
return block
|
||||
}
|
||||
|
||||
_handleClick(selector) {
|
||||
const block = new Block(this._frameId);
|
||||
const block = new Block(this._frameId)
|
||||
if (this._options.waitForSelectorOnClick) {
|
||||
block.addLine({
|
||||
type: domEvents.CLICK,
|
||||
value: `await ${this._frame}.waitForSelector('${selector}')`
|
||||
});
|
||||
value: `await ${this._frame}.waitForSelector('${selector}')`,
|
||||
})
|
||||
}
|
||||
block.addLine({
|
||||
type: domEvents.CLICK,
|
||||
value: `await ${this._frame}.click('${selector}')`
|
||||
});
|
||||
return block;
|
||||
value: `await ${this._frame}.click('${selector}')`,
|
||||
})
|
||||
return block
|
||||
}
|
||||
|
||||
_handleChange(selector, value) {
|
||||
return new Block(this._frameId, {
|
||||
type: domEvents.CHANGE,
|
||||
value: `await ${this._frame}.select('${selector}', '${value}')`
|
||||
});
|
||||
value: `await ${this._frame}.select('${selector}', '${value}')`,
|
||||
})
|
||||
}
|
||||
|
||||
_handleGoto(href) {
|
||||
return new Block(this._frameId, {
|
||||
type: pptrActions.GOTO,
|
||||
value: `await ${this._frame}.goto('${href}')`
|
||||
});
|
||||
value: `await ${this._frame}.goto('${href}')`,
|
||||
})
|
||||
}
|
||||
|
||||
_handleViewport() {
|
||||
throw new Error("Not implemented.");
|
||||
throw new Error('Not implemented.')
|
||||
}
|
||||
|
||||
_handleScreenshot(options) {
|
||||
let block;
|
||||
let block
|
||||
|
||||
if (options && options.x && options.y && options.width && options.height) {
|
||||
// remove the tailing 'px'
|
||||
for (let prop in options) {
|
||||
if (options.hasOwnProperty(prop) && options[prop].slice(-2) === "px") { // eslint-disable-line
|
||||
options[prop] = options[prop].substring(0, options[prop].length - 2);
|
||||
options[prop] = options[prop].substring(0, options[prop].length - 2)
|
||||
}
|
||||
}
|
||||
|
||||
block = new Block(this._frameId, {
|
||||
type: pptrActions.SCREENSHOT,
|
||||
value: `await ${this._frame}.screenshot({ path: 'screenshot_${this._screenshotCounter}.png', clip: { x: ${options.x}, y: ${options.y}, width: ${options.width}, height: ${options.height} } })`
|
||||
});
|
||||
value: `await ${this._frame}.screenshot({ path: 'screenshot_${this._screenshotCounter}.png', clip: { x: ${options.x}, y: ${options.y}, width: ${options.width}, height: ${options.height} } })`,
|
||||
})
|
||||
} else {
|
||||
block = new Block(this._frameId, {
|
||||
type: pptrActions.SCREENSHOT,
|
||||
value: `await ${this._frame}.screenshot({ path: 'screenshot_${this._screenshotCounter}.png' })`
|
||||
});
|
||||
value: `await ${this._frame}.screenshot({ path: 'screenshot_${this._screenshotCounter}.png' })`,
|
||||
})
|
||||
}
|
||||
|
||||
this._screenshotCounter++;
|
||||
return block;
|
||||
this._screenshotCounter++
|
||||
return block
|
||||
}
|
||||
|
||||
_handleWaitForNavigation() {
|
||||
const block = new Block(this._frameId);
|
||||
const block = new Block(this._frameId)
|
||||
if (this._options.waitForNavigation) {
|
||||
block.addLine({
|
||||
type: pptrActions.NAVIGATION,
|
||||
value: `await navigationPromise`
|
||||
});
|
||||
value: `await navigationPromise`,
|
||||
})
|
||||
}
|
||||
return block;
|
||||
return block
|
||||
}
|
||||
|
||||
_postProcessSetFrames() {
|
||||
for (let [i, block] of this._blocks.entries()) {
|
||||
const lines = block.getLines();
|
||||
const lines = block.getLines()
|
||||
for (let line of lines) {
|
||||
if (
|
||||
line.frameId &&
|
||||
@@ -236,33 +236,33 @@ export default class CodeGenerator {
|
||||
) {
|
||||
const declaration = `const frame_${
|
||||
line.frameId
|
||||
} = frames.find(f => f.url() === '${this._allFrames[line.frameId]}')`;
|
||||
} = frames.find(f => f.url() === '${this._allFrames[line.frameId]}')`
|
||||
this._blocks[i].addLineToTop({
|
||||
type: pptrActions.FRAME_SET,
|
||||
value: declaration
|
||||
});
|
||||
value: declaration,
|
||||
})
|
||||
this._blocks[i].addLineToTop({
|
||||
type: pptrActions.FRAME_SET,
|
||||
value: "let frames = await page.frames()"
|
||||
});
|
||||
delete this._allFrames[line.frameId];
|
||||
break;
|
||||
value: 'let frames = await page.frames()',
|
||||
})
|
||||
delete this._allFrames[line.frameId]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_postProcessAddBlankLines() {
|
||||
let i = 0;
|
||||
let i = 0
|
||||
while (i <= this._blocks.length) {
|
||||
const blankLine = new Block();
|
||||
blankLine.addLine({ type: null, value: "" });
|
||||
this._blocks.splice(i, 0, blankLine);
|
||||
i += 2;
|
||||
const blankLine = new Block()
|
||||
blankLine.addLine({ type: null, value: '' })
|
||||
this._blocks.splice(i, 0, blankLine)
|
||||
i += 2
|
||||
}
|
||||
}
|
||||
|
||||
_escapeUserInput(value) {
|
||||
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
||||
return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import pptrActions from "./pptr-actions";
|
||||
import Block from "./Block";
|
||||
import CodeGenerator from "./CodeGenerator";
|
||||
import pptrActions from './pptr-actions'
|
||||
import Block from './Block'
|
||||
import CodeGenerator from './CodeGenerator'
|
||||
|
||||
const importPlaywright = `const { chromium } = require('playwright');\n`;
|
||||
const importPlaywright = `const { chromium } = require('playwright');\n`
|
||||
|
||||
const header = `const browser = await chromium.launch()
|
||||
const context = await browser.newContext()
|
||||
const page = await context.newPage()`;
|
||||
const page = await context.newPage()`
|
||||
|
||||
const footer = `await browser.close()`;
|
||||
const footer = `await browser.close()`
|
||||
|
||||
const wrappedHeader = `(async () => {
|
||||
${header}\n`;
|
||||
${header}\n`
|
||||
|
||||
const wrappedFooter = ` ${footer}
|
||||
})()`;
|
||||
})()`
|
||||
|
||||
export default class PlaywrightCodeGenerator extends CodeGenerator {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this._header = header;
|
||||
this._wrappedHeader = wrappedHeader;
|
||||
this._footer = footer;
|
||||
this._wrappedFooter = wrappedFooter;
|
||||
super(options)
|
||||
this._header = header
|
||||
this._wrappedHeader = wrappedHeader
|
||||
this._footer = footer
|
||||
this._wrappedFooter = wrappedFooter
|
||||
}
|
||||
|
||||
generate(events) {
|
||||
@@ -31,20 +31,20 @@ export default class PlaywrightCodeGenerator extends CodeGenerator {
|
||||
this._getHeader() +
|
||||
this._parseEvents(events) +
|
||||
this._getFooter()
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
_handleViewport(width, height) {
|
||||
return new Block(this._frameId, {
|
||||
type: pptrActions.VIEWPORT,
|
||||
value: `await ${this._frame}.setViewportSize({ width: ${width}, height: ${height} })`
|
||||
});
|
||||
value: `await ${this._frame}.setViewportSize({ width: ${width}, height: ${height} })`,
|
||||
})
|
||||
}
|
||||
|
||||
_handleChange(selector, value) {
|
||||
return new Block(this._frameId, {
|
||||
type: pptrActions.CHANGE,
|
||||
value: `await ${this._frame}.selectOption('${selector}', '${value}')`
|
||||
});
|
||||
value: `await ${this._frame}.selectOption('${selector}', '${value}')`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import pptrActions from "./pptr-actions";
|
||||
import Block from "./Block";
|
||||
import CodeGenerator from "./CodeGenerator";
|
||||
import pptrActions from './pptr-actions'
|
||||
import Block from './Block'
|
||||
import CodeGenerator from './CodeGenerator'
|
||||
|
||||
const importPuppeteer = `const puppeteer = require('puppeteer');\n`;
|
||||
const importPuppeteer = `const puppeteer = require('puppeteer');\n`
|
||||
|
||||
const header = `const browser = await puppeteer.launch()
|
||||
const page = await browser.newPage()`;
|
||||
const page = await browser.newPage()`
|
||||
|
||||
const footer = `await browser.close()`;
|
||||
const footer = `await browser.close()`
|
||||
|
||||
const wrappedHeader = `(async () => {
|
||||
const browser = await puppeteer.launch()
|
||||
const page = await browser.newPage()\n`;
|
||||
const page = await browser.newPage()\n`
|
||||
|
||||
const wrappedFooter = ` await browser.close()
|
||||
})()`;
|
||||
})()`
|
||||
|
||||
export default class PuppeteerCodeGenerator extends CodeGenerator {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this._header = header;
|
||||
this._wrappedHeader = wrappedHeader;
|
||||
this._footer = footer;
|
||||
this._wrappedFooter = wrappedFooter;
|
||||
super(options)
|
||||
this._header = header
|
||||
this._wrappedHeader = wrappedHeader
|
||||
this._footer = footer
|
||||
this._wrappedFooter = wrappedFooter
|
||||
}
|
||||
|
||||
generate(events) {
|
||||
@@ -31,13 +31,13 @@ export default class PuppeteerCodeGenerator extends CodeGenerator {
|
||||
this._getHeader() +
|
||||
this._parseEvents(events) +
|
||||
this._getFooter()
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
_handleViewport(width, height) {
|
||||
return new Block(this._frameId, {
|
||||
type: pptrActions.VIEWPORT,
|
||||
value: `await ${this._frame}.setViewport({ width: ${width}, height: ${height} })`
|
||||
});
|
||||
value: `await ${this._frame}.setViewport({ width: ${width}, height: ${height} })`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
export default {
|
||||
CLICK: "click",
|
||||
DBLCLICK: "dblclick",
|
||||
CHANGE: "change",
|
||||
KEYDOWN: "keydown",
|
||||
SELECT: "select",
|
||||
SUBMIT: "submit",
|
||||
LOAD: "load",
|
||||
UNLOAD: "unload"
|
||||
};
|
||||
CLICK: 'click',
|
||||
DBLCLICK: 'dblclick',
|
||||
CHANGE: 'change',
|
||||
KEYDOWN: 'keydown',
|
||||
SELECT: 'select',
|
||||
SUBMIT: 'submit',
|
||||
LOAD: 'load',
|
||||
UNLOAD: 'unload',
|
||||
}
|
||||
|
||||
// const events = [
|
||||
// abort,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export default {
|
||||
GOTO: "GOTO",
|
||||
VIEWPORT: "VIEWPORT",
|
||||
WAITFORSELECTOR: "WAITFORSELECTOR",
|
||||
NAVIGATION: "NAVIGATION",
|
||||
NAVIGATION_PROMISE: "NAVIGATION_PROMISE",
|
||||
FRAME_SET: "FRAME_SET",
|
||||
SCREENSHOT: "SCREENSHOT"
|
||||
};
|
||||
GOTO: 'GOTO',
|
||||
VIEWPORT: 'VIEWPORT',
|
||||
WAITFORSELECTOR: 'WAITFORSELECTOR',
|
||||
NAVIGATION: 'NAVIGATION',
|
||||
NAVIGATION_PROMISE: 'NAVIGATION_PROMISE',
|
||||
FRAME_SET: 'FRAME_SET',
|
||||
SCREENSHOT: 'SCREENSHOT',
|
||||
}
|
||||
|
||||
1649
tailwind.config.js
1649
tailwind.config.js
File diff suppressed because it is too large
Load Diff
@@ -1,28 +1,28 @@
|
||||
module.exports = {
|
||||
pages: {
|
||||
popup: {
|
||||
template: "public/browser-extension.html",
|
||||
entry: "./src/popup/main.js",
|
||||
title: "Popup"
|
||||
template: 'public/browser-extension.html',
|
||||
entry: './src/popup/main.js',
|
||||
title: 'Popup',
|
||||
},
|
||||
options: {
|
||||
template: "public/browser-extension.html",
|
||||
entry: "./src/options/main.js",
|
||||
title: "Options"
|
||||
}
|
||||
template: 'public/browser-extension.html',
|
||||
entry: './src/options/main.js',
|
||||
title: 'Options',
|
||||
},
|
||||
},
|
||||
pluginOptions: {
|
||||
browserExtension: {
|
||||
componentOptions: {
|
||||
background: {
|
||||
entry: "src/background.js"
|
||||
entry: 'src/background.js',
|
||||
},
|
||||
contentScripts: {
|
||||
entries: {
|
||||
"content-script": ["src/content-scripts/index.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
'content-script': ['src/content-scripts/index.js'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user