Skip to content

Commit

Permalink
take screenshots on failure in side-runner
Browse files Browse the repository at this point in the history
  • Loading branch information
toddtarsi committed Jun 12, 2023
1 parent 2fbd505 commit 9ff0917
Show file tree
Hide file tree
Showing 8 changed files with 994 additions and 124 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = {
'plugin:react/recommended',
'prettier',
],
parser: 'babel-eslint',
parser: '@babel/eslint-parser',
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
Expand Down
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"devDependencies": {
"@babel/cli": "^7.17.6",
"@babel/core": "^7.17.8",
"@babel/eslint-parser": "7.22.5",
"@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-export-namespace-from": "^7.16.7",
"@babel/preset-env": "^7.16.11",
Expand All @@ -48,8 +49,7 @@
"@types/node": "^16.11.26",
"@typescript-eslint/eslint-plugin": "^5.15.0",
"@typescript-eslint/parser": "^5.15.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^27.5.1",
"babel-jest": "^29.5.0",
"eslint": "^8.11.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^2.5.0",
Expand All @@ -63,8 +63,8 @@
"identity-obj-proxy": "^3.0.0",
"jasmine": "^4.0.2",
"jasmine-core": "^4.0.1",
"jest": "^27.5.1",
"jest-cli": "27.5.1",
"jest": "^29.5.0",
"jest-cli": "^29.5.0",
"npm-run-all": "^4.1.5",
"npm-run-bg": "^1.0.7",
"prettier": "^2.6.0",
Expand All @@ -73,7 +73,7 @@
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-standard": "^25.0.0",
"stylelint-prettier": "^2.0.0",
"ts-jest": "^27.1.3",
"typescript": "^4.8.3"
"ts-jest": "^29.1.0",
"typescript": "^5.1.3"
}
}
2 changes: 1 addition & 1 deletion packages/selenium-ide/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@
"style-loader": "^3.1.0",
"ts-loader": "^9.2.3",
"ts-node": "^10.7.0",
"typescript": "^4.5.4",
"typescript": "^5.1.3",
"ua-parser-js": "^0.7.19",
"url-loader": "^4.1.1",
"webpack": "^5.70.0",
Expand Down
14 changes: 13 additions & 1 deletion packages/side-runner/src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,13 @@ program
'Use specified YAML file for configuration. (default: .side.yml)'
)
.option(
'-o, --output-directory [it directory]',
'-o, --output-directory [directory]',
'Write test results as json to file in specified directory. Name will be based on timestamp.'
)
.option(
'-z, --screenshot-failure-directory [directory]',
'Write screenshots of failed tests to file in specified directory. Name will be based on test + timestamp.'
)
.option(
'-f, --force',
"Forcibly run the project, regardless of project's version"
Expand All @@ -103,6 +107,7 @@ if (!program.args.length) {
process.exit(1)
}
const options = program.opts()

let configuration: Configuration = {
baseUrl: '',
capabilities: {
Expand All @@ -113,6 +118,7 @@ let configuration: Configuration = {
filter: options.filter || '.*',
force: options.force,
maxWorkers: os.cpus().length,
screenshotFailureDirectory: options.screenshotFailureDirectory,
// Convert all project paths into absolute paths
projects: [],
proxyOptions: {},
Expand Down Expand Up @@ -175,6 +181,12 @@ if (options.outputDirectory) {
}
}

if (options.screenshotFailureDirectory) {
if (!fs.existsSync(options.screenshotFailureDirectory)) {
fs.mkdirSync(options.screenshotFailureDirectory, { recursive: true })
}
}

configuration.debugStartup &&
console.debug('Configuration:', util.inspect(configuration))

Expand Down
22 changes: 22 additions & 0 deletions packages/side-runner/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
} from '@seleniumhq/side-runtime'
import { WebDriverExecutorConstructorArgs } from '@seleniumhq/side-runtime/dist/webdriver'
import { SuiteShape, TestShape } from '@seleniumhq/side-model'
import fs from 'fs/promises'
import path from 'path'
import Satisfies from './versioner'
import { Configuration, Project } from './types'

Expand Down Expand Up @@ -73,6 +75,26 @@ const buildRunners = ({ configuration, logger }: HoistedThings) => {
variables: new Variables(),
})
const onComplete = async (failure: any) => {
// I know this feature is over aggressive for a tool to be deprecated
// but I can't figure out whats going wrong at {{paid work}} and I
// need this so just please don't ask me to expand on it because I
// don't want to own screenshotting in tool tbh. That is perfect for
// plugins.
if (failure && configuration.screenshotFailureDirectory) {
try {
const crashScreen = await driver.driver.takeScreenshot()
await fs.writeFile(
path.join(
configuration.screenshotFailureDirectory,
`${test.name}_${Date.now()}.png`
),
crashScreen,
'base64'
)
} catch (e) {
console.log('Failed to take screenshot', e)
}
}
await playback.cleanup()
await driver.cleanup()
if (failure) {
Expand Down
1 change: 1 addition & 0 deletions packages/side-runner/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,5 @@ export type Configuration = Required<
proxyOptions: ProxyInputOptions
runId: string
path: string
screenshotFailureDirectory?: string
}
30 changes: 16 additions & 14 deletions packages/side-runtime/src/webdriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,19 @@ export interface WindowSwitchedHookInput {
windowHandle?: string | Error
}

export type GeneralHook<T> = (input: T) => Promise<void> | void

export interface WebDriverExecutorHooks {
onBeforePlay?: (input: BeforePlayHookInput) => Promise<void> | void
onAfterCommand?: (input: CommandHookInput) => Promise<void> | void
onBeforeCommand?: (input: CommandHookInput) => Promise<void> | void
onStoreWindowHandle?: (
input?: StoreWindowHandleHookInput
) => Promise<void> | void
onWindowAppeared?: (input: WindowAppearedHookInput) => Promise<void> | void
onWindowSwitched?: (input: WindowSwitchedHookInput) => Promise<void> | void
onBeforePlay?: GeneralHook<BeforePlayHookInput>
onAfterCommand?: GeneralHook<CommandHookInput>
onBeforeCommand?: GeneralHook<CommandHookInput>
onStoreWindowHandle?: GeneralHook<StoreWindowHandleHookInput>
onWindowAppeared?: GeneralHook<WindowAppearedHookInput>
onWindowSwitched?: GeneralHook<WindowSwitchedHookInput>
}

export type HookKeys = keyof WebDriverExecutorHooks

export interface ElementEditableScriptResult {
enabled: boolean
readonly: boolean
Expand Down Expand Up @@ -241,16 +243,16 @@ export default class WebDriverExecutor {
return func
}

async executeHook<T extends keyof WebDriverExecutorHooks>(
async executeHook<T extends HookKeys>(
hook: T,
...args: Parameters<NonNullable<WebDriverExecutorHooks[T]>>
) {
const fn = this.hooks[hook] as WebDriverExecutorHooks[T]
type HookContents = WebDriverExecutorHooks[T]
type HookParameters = Parameters<NonNullable<HookContents>>
const fn = this.hooks[hook] as HookContents
if (!fn) return
await fn.apply(
this,
args as Parameters<NonNullable<WebDriverExecutorHooks[T]>>
)
// @ts-expect-error it's okay, this shape is fine
await fn.apply(this, args as HookParameters)
}

async beforeCommand(commandObject: CommandShape) {
Expand Down
Loading

2 comments on commit 9ff0917

@bbulter99a
Copy link

@bbulter99a bbulter99a commented on 9ff0917 Feb 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be deprecated? How do you know this? Or when do you think this will happen? Thanks for any info.

@toddtarsi
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh?

Please sign in to comment.