fix test script and video capture
This commit is contained in:
@@ -2,7 +2,7 @@ import * as fs from 'fs'
|
||||
import * as os from 'os'
|
||||
import * as path from 'path'
|
||||
|
||||
import { ElectronApplication, Page, _electron as electron } from 'playwright'
|
||||
import { ElectronApplication, _electron as electron } from 'playwright'
|
||||
|
||||
import mockMqtt, { stop as stopMqtt } from './mock-mqtt'
|
||||
import { clearOldTopics } from './scenarios/clearOldTopics'
|
||||
@@ -13,7 +13,7 @@ import { copyTopicToClipboard } from './scenarios/copyTopicToClipboard'
|
||||
import { copyValueToClipboard } from './scenarios/copyValueToClipboard'
|
||||
import { disconnect } from './scenarios/disconnect'
|
||||
import { publishTopic } from './scenarios/publishTopic'
|
||||
import { SceneBuilder } from './SceneBuilder'
|
||||
import { Scene, SceneBuilder } from './SceneBuilder'
|
||||
import { showAdvancedConnectionSettings } from './scenarios/showAdvancedConnectionSettings'
|
||||
import { showJsonPreview } from './scenarios/showJsonPreview'
|
||||
import { showMenu } from './scenarios/showMenu'
|
||||
@@ -21,6 +21,15 @@ import { showNumericPlot } from './scenarios/showNumericPlot'
|
||||
import { showOffDiffCapability } from './scenarios/showOffDiffCapability'
|
||||
import { showZoomLevel } from './scenarios/showZoomLevel'
|
||||
|
||||
/**
|
||||
* A convenience method that handles gracefully cleaning up the test run.
|
||||
*/
|
||||
const cleanUp = async (scenes: SceneBuilder, electronApp: ElectronApplication) => {
|
||||
// Exit app.
|
||||
fs.writeFileSync('scenes.json', JSON.stringify(scenes.scenes, undefined, ' '))
|
||||
await electronApp.close()
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', (error: Error | any) => {
|
||||
console.error('unhandledRejection', error.message, error.stack)
|
||||
process.exit(1)
|
||||
@@ -36,16 +45,10 @@ async function doStuff() {
|
||||
|
||||
// Launch Electron app.
|
||||
const electronApp: ElectronApplication = await electron.launch({
|
||||
args: [`${__dirname}/../../..`],
|
||||
// recordVideo: {
|
||||
// dir: path.join(__dirname, '..', 'playwright-videos'),
|
||||
// size: {
|
||||
// width: 1280,
|
||||
// height: 720,
|
||||
// },
|
||||
// },
|
||||
args: [`${__dirname}/../../..`, ...runningUiTestOnCi],
|
||||
})
|
||||
|
||||
console.log('Playwright started')
|
||||
// Get the first window that the app opens, wait if necessary.
|
||||
const page = await electronApp.firstWindow({ timeout: 3000 })
|
||||
// Print the title.
|
||||
@@ -138,14 +141,10 @@ async function doStuff() {
|
||||
await sleep(3000)
|
||||
})
|
||||
|
||||
// Exit app.
|
||||
await electronApp.close()
|
||||
console.log('Electron exited')
|
||||
|
||||
stopMqtt()
|
||||
console.log('Stopped mqtt')
|
||||
|
||||
fs.writeFileSync('scenes.json', JSON.stringify(scenes.scenes, undefined, ' '))
|
||||
cleanUp(scenes, electronApp)
|
||||
}
|
||||
|
||||
doStuff()
|
||||
|
||||
@@ -1,131 +1,114 @@
|
||||
// import * as os from 'os'
|
||||
// import mockMqtt, { stopUpdates as stopMqttUpdates } from './mock-mqtt'
|
||||
// import { ClassNameMapping, countInstancesOf, createFakeMousePointer, getHeapDump, setFast, sleep } from './util'
|
||||
// import { clearSearch, searchTree } from './scenarios/searchTree'
|
||||
// import { connectTo } from './scenarios/connect'
|
||||
// import { reconnect } from './scenarios/reconnect'
|
||||
import * as os from 'os'
|
||||
import { ElectronApplication, _electron as electron } from 'playwright'
|
||||
|
||||
// process.on('unhandledRejection', (error: Error | any) => {
|
||||
// console.error('unhandledRejection', error.message, error.stack)
|
||||
// process.exit(1)
|
||||
// })
|
||||
import mockMqtt, { stopUpdates as stopMqttUpdates } from './mock-mqtt'
|
||||
import { ClassNameMapping, countInstancesOf, createFakeMousePointer, getHeapDump, setFast, sleep } from './util'
|
||||
import { clearSearch, searchTree } from './scenarios/searchTree'
|
||||
import { connectTo } from './scenarios/connect'
|
||||
import { reconnect } from './scenarios/reconnect'
|
||||
|
||||
// const runningUiTestOnCi = os.platform() === 'darwin' ? [] : ['--runningUiTestOnCi']
|
||||
process.on('unhandledRejection', (error: Error | any) => {
|
||||
console.error('unhandledRejection', error.message, error.stack)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
// const options = {
|
||||
// host: '127.0.0.1', // Use localhost as chrome driver server
|
||||
// port: 9515, // "9515" is the port opened by chrome driver.
|
||||
// capabilities: {
|
||||
// browserName: 'chrome',
|
||||
// 'goog:chromeOptions': {
|
||||
// binary: `${__dirname}/../../../node_modules/.bin/electron`,
|
||||
// args: [
|
||||
// `--app=${__dirname}/../../..`,
|
||||
// '--force-device-scale-factor=1',
|
||||
// '--no-sandbox',
|
||||
// '--disable-dev-shm-usage',
|
||||
// '--disable-extensions',
|
||||
// ].concat(runningUiTestOnCi),
|
||||
// windowTypes: ['app', 'webview'],
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
const runningUiTestOnCi = os.platform() === 'darwin' ? [] : ['--runningUiTestOnCi']
|
||||
|
||||
async function doStuff() {
|
||||
// console.log('Waiting for MQTT Broker on port 1880 (no auth)')
|
||||
// await mockMqtt()
|
||||
// console.log('start webdriver')
|
||||
console.log('Waiting for MQTT Broker on port 1880 (no auth)')
|
||||
await mockMqtt()
|
||||
|
||||
// const browser = await webdriverio.remote(options)
|
||||
// setFast()
|
||||
// await createFakeMousePointer(browser)
|
||||
console.log('Starting playwright/electron')
|
||||
|
||||
// // Wait for Username input to be visible
|
||||
// await browser.$('//label[contains(text(), "Username")]/..//input')
|
||||
// await connectTo('127.0.0.1', browser)
|
||||
// stopMqttUpdates()
|
||||
// await sleep(1000, true)
|
||||
// Launch Electron app.
|
||||
const electronApp: ElectronApplication = await electron.launch({
|
||||
args: [`${__dirname}/../../..`, ...runningUiTestOnCi],
|
||||
})
|
||||
|
||||
// const heapDump = await getHeapDump(browser)
|
||||
// const initialTreeOccurrences = await countInstancesOf(heapDump, ClassNameMapping.Tree)
|
||||
// const initialNodeOccurrences = await countInstancesOf(heapDump, ClassNameMapping.TreeNode)
|
||||
// console.log(initialTreeOccurrences, initialNodeOccurrences)
|
||||
console.log('Playwright started')
|
||||
// Get the first window that the app opens, wait if necessary.
|
||||
const browser = await electronApp.firstWindow({ timeout: 3000 })
|
||||
// Print the title.
|
||||
console.log(await browser.title())
|
||||
// Capture a screenshot.
|
||||
await browser.screenshot({ path: 'intro.png' })
|
||||
// Direct Electron console to Node terminal.
|
||||
browser.on('console', console.log)
|
||||
|
||||
// await doX(3, async () => {
|
||||
// await reconnect(browser)
|
||||
// })
|
||||
|
||||
// await sleep(1000, true)
|
||||
|
||||
// await doX(15, async () => {
|
||||
// await searchTree('temp', browser)
|
||||
// await reconnect(browser)
|
||||
// })
|
||||
|
||||
// await searchTree('ab', browser)
|
||||
// await clearSearch(browser)
|
||||
|
||||
// await searchTree('temp', browser)
|
||||
// await clearSearch(browser)
|
||||
|
||||
// await sleep(1000, true)
|
||||
|
||||
// await waitForGarbageCollectorToDetermineLeak(browser, initialTreeOccurrences, initialNodeOccurrences)
|
||||
// }
|
||||
|
||||
// async function waitForGarbageCollectorToDetermineLeak(
|
||||
// browser: any,
|
||||
// initialTreeOccurrences: number,
|
||||
// initialNodeOccurrences: number
|
||||
// ) {
|
||||
// let delta = -1
|
||||
// let lastTreeOccurrences = -1
|
||||
// let lastNodeOccurrences = -1
|
||||
// let leak = false
|
||||
// while (delta < 0) {
|
||||
// if (lastTreeOccurrences !== -1) {
|
||||
// await sleep(10000, true)
|
||||
// }
|
||||
// const heapDump = await getHeapDump(browser)
|
||||
// const currentTreeOccurrences = await countInstancesOf(heapDump, ClassNameMapping.Tree)
|
||||
// const currentNodeOccurrences = await countInstancesOf(heapDump, ClassNameMapping.TreeNode)
|
||||
|
||||
// // Temporary "leaks" are expected due to React Fibers memoization
|
||||
// if (
|
||||
// Math.abs(initialTreeOccurrences - currentTreeOccurrences) > 1 ||
|
||||
// Math.abs(currentNodeOccurrences - initialNodeOccurrences) > 8
|
||||
// ) {
|
||||
// console.error(
|
||||
// 'Possible leak detected',
|
||||
// initialTreeOccurrences,
|
||||
// currentTreeOccurrences,
|
||||
// initialNodeOccurrences,
|
||||
// currentNodeOccurrences
|
||||
// )
|
||||
// leak = true
|
||||
// } else {
|
||||
// leak = false
|
||||
// }
|
||||
|
||||
// const treeDelta = lastTreeOccurrences >= 0 ? currentTreeOccurrences - lastTreeOccurrences : -1
|
||||
// const nodeDelta = lastTreeOccurrences >= 0 ? currentNodeOccurrences - lastNodeOccurrences : -1
|
||||
// delta = treeDelta + nodeDelta
|
||||
|
||||
// lastTreeOccurrences = currentTreeOccurrences
|
||||
// lastNodeOccurrences = currentNodeOccurrences
|
||||
// }
|
||||
|
||||
// if (leak) {
|
||||
// console.error('leak')
|
||||
// process.exit(100)
|
||||
// }
|
||||
// }
|
||||
|
||||
// async function doX(x: number, action: () => Promise<any>) {
|
||||
// for (let i = 0; i < x; i += 1) {
|
||||
// await action()
|
||||
// await sleep(10, true)
|
||||
// }
|
||||
setFast()
|
||||
await createFakeMousePointer(browser)
|
||||
// Wait for Username input to be visible
|
||||
await browser.locator('//label[contains(text(), "Username")]/..//input')
|
||||
await connectTo('127.0.0.1', browser)
|
||||
stopMqttUpdates()
|
||||
await sleep(1000, true)
|
||||
const heapDump = await getHeapDump(browser)
|
||||
const initialTreeOccurrences = await countInstancesOf(heapDump, ClassNameMapping.Tree)
|
||||
const initialNodeOccurrences = await countInstancesOf(heapDump, ClassNameMapping.TreeNode)
|
||||
console.log(initialTreeOccurrences, initialNodeOccurrences)
|
||||
await doX(3, async () => {
|
||||
await reconnect(browser)
|
||||
})
|
||||
await sleep(1000, true)
|
||||
await doX(15, async () => {
|
||||
await searchTree('temp', browser)
|
||||
await reconnect(browser)
|
||||
})
|
||||
await searchTree('ab', browser)
|
||||
await clearSearch(browser)
|
||||
await searchTree('temp', browser)
|
||||
await clearSearch(browser)
|
||||
await sleep(1000, true)
|
||||
await waitForGarbageCollectorToDetermineLeak(browser, initialTreeOccurrences, initialNodeOccurrences)
|
||||
}
|
||||
async function waitForGarbageCollectorToDetermineLeak(
|
||||
browser: any,
|
||||
initialTreeOccurrences: number,
|
||||
initialNodeOccurrences: number
|
||||
) {
|
||||
let delta = -1
|
||||
let lastTreeOccurrences = -1
|
||||
let lastNodeOccurrences = -1
|
||||
let leak = false
|
||||
while (delta < 0) {
|
||||
if (lastTreeOccurrences !== -1) {
|
||||
await sleep(10000, true)
|
||||
}
|
||||
const heapDump = await getHeapDump(browser)
|
||||
const currentTreeOccurrences = await countInstancesOf(heapDump, ClassNameMapping.Tree)
|
||||
const currentNodeOccurrences = await countInstancesOf(heapDump, ClassNameMapping.TreeNode)
|
||||
// Temporary "leaks" are expected due to React Fibers memoization
|
||||
if (
|
||||
Math.abs(initialTreeOccurrences - currentTreeOccurrences) > 1 ||
|
||||
Math.abs(currentNodeOccurrences - initialNodeOccurrences) > 8
|
||||
) {
|
||||
console.error(
|
||||
'Possible leak detected',
|
||||
initialTreeOccurrences,
|
||||
currentTreeOccurrences,
|
||||
initialNodeOccurrences,
|
||||
currentNodeOccurrences
|
||||
)
|
||||
leak = true
|
||||
} else {
|
||||
leak = false
|
||||
}
|
||||
const treeDelta = lastTreeOccurrences >= 0 ? currentTreeOccurrences - lastTreeOccurrences : -1
|
||||
const nodeDelta = lastTreeOccurrences >= 0 ? currentNodeOccurrences - lastNodeOccurrences : -1
|
||||
delta = treeDelta + nodeDelta
|
||||
lastTreeOccurrences = currentTreeOccurrences
|
||||
lastNodeOccurrences = currentNodeOccurrences
|
||||
}
|
||||
if (leak) {
|
||||
console.error('leak')
|
||||
process.exit(100)
|
||||
}
|
||||
}
|
||||
async function doX(x: number, action: () => Promise<any>) {
|
||||
for (let i = 0; i < x; i += 1) {
|
||||
await action()
|
||||
await sleep(10, true)
|
||||
}
|
||||
}
|
||||
|
||||
doStuff()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Browser, Element } from 'webdriverio'
|
||||
import { Page } from 'playwright'
|
||||
import { clickOn } from '../util'
|
||||
|
||||
export async function reconnect(browser: Browser<'async'>) {
|
||||
const disconnectButton = await browser.$('//button/span[contains(text(),"Disconnect")]')
|
||||
await clickOn(disconnectButton, browser)
|
||||
const connectButton = await browser.$('//button/span[contains(text(),"Connect")]')
|
||||
await clickOn(connectButton, browser)
|
||||
export async function reconnect(browser: Page) {
|
||||
const disconnectButton = await browser.locator('//button/span[contains(text(),"Disconnect")]')
|
||||
await clickOn(disconnectButton)
|
||||
const connectButton = await browser.locator('//button/span[contains(text(),"Connect")]')
|
||||
await clickOn(connectButton)
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export async function setTextInInput(name: string, text: string, browser: Page)
|
||||
|
||||
export async function moveToCenterOfElement(element: Locator) {
|
||||
// @ts-ignore
|
||||
const { x, y, width, height } = await element.boundingBox();
|
||||
const { x, y, width, height } = await element.boundingBox()
|
||||
|
||||
const targetX = x + width / 2
|
||||
const targetY = y + height / 2
|
||||
@@ -71,8 +71,9 @@ export async function moveToCenterOfElement(element: Locator) {
|
||||
}
|
||||
|
||||
export async function runJavascript(js: string, browser: Page) {
|
||||
console.log(js)
|
||||
await browser.evaluate(_js => eval(_js), js)
|
||||
// there is probably a safer way to do this.. do not use eval...
|
||||
// tslint:disable-next-line no-eval
|
||||
return browser.evaluate(script => eval(script), js)
|
||||
}
|
||||
|
||||
export async function clickOnHistory(browser: Page) {
|
||||
@@ -80,16 +81,22 @@ export async function clickOnHistory(browser: Page) {
|
||||
await clickOn(messageHistory)
|
||||
}
|
||||
|
||||
export async function clickOn(element: Locator, clicks = 1, delay = 0, button: 'left' | 'right' | 'middle' = 'left', force = false) {
|
||||
export async function clickOn(
|
||||
element: Locator,
|
||||
clicks = 1,
|
||||
delay = 0,
|
||||
button: 'left' | 'right' | 'middle' = 'left',
|
||||
force = false
|
||||
) {
|
||||
await moveToCenterOfElement(element)
|
||||
await element.hover()
|
||||
await element.click({ clickCount: clicks, delay, button, force })
|
||||
await element.click({ delay, button, force, clickCount: clicks })
|
||||
await sleep(50)
|
||||
}
|
||||
|
||||
export async function createFakeMousePointer(browser: Page) {
|
||||
const js = 'window.demo.enableMouse();'
|
||||
|
||||
// @ts-lint-ignore
|
||||
await runJavascript(js, browser)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user