Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(webdriverio): fix types between v8 and <v8 #962

Merged
merged 6 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
- run: npm ci
# HACK: Force a TTY to enable browser-driver-manager to manipulate stdout.
- shell: 'script -q -e -c "bash {0}"'
run: npx browser-driver-manager install chromedriver --verbose
run: npx browser-driver-manager install chromedriver=119 --verbose
Copy link
Contributor Author

@straker straker Dec 20, 2023

Choose a reason for hiding this comment

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

This is a temporary fix that will be fixed in a separate pr to fix the cli tests with 120 #963.

working-directory: packages/cli
- run: npm run coverage --workspace=packages/cli

Expand Down
1,825 changes: 441 additions & 1,384 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion packages/webdriverio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@
"@types/chromedriver": "^81.0.1",
"@types/cssesc": "^3.0.0",
"@types/express": "^4.17.14",
"@types/mocha": "^10.0.0",
"@types/mocha": "^10.0.6",
"@types/node": "^20.8.10",
"@wdio/globals": "^8.27.0",
"async-listen": "^3.0.1",
"axe-test-fixtures": "github:dequelabs/axe-test-fixtures#v1",
"chai": "^4.3.6",
Expand Down
25 changes: 16 additions & 9 deletions packages/webdriverio/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@ import {
import { getFilename } from 'cross-dirname';
import { pathToFileURL } from 'url';

import type { Browser, Element } from 'webdriverio';
import type {
RunOptions,
AxeResults,
SerialContextObject,
SerialSelectorList,
SerialFrameSelector
} from 'axe-core';
import type { Options, CallbackFunction, PartialResults } from './types';
import type {
Options,
CallbackFunction,
PartialResults,
WdioBrowser,
WdioElement
} from './types';

let axeCorePath = '';
async function loadAxePath() {
Expand All @@ -43,7 +48,7 @@ async function loadAxePath() {
loadAxePath();

export default class AxeBuilder {
private client: Browser;
private client: WdioBrowser;
private axeSource: string;
private includes: SerialSelectorList = [];
private excludes: SerialSelectorList = [];
Expand Down Expand Up @@ -199,7 +204,9 @@ export default class AxeBuilder {
/**
* Injects `axe-core` into all frames.
*/
private async inject(browsingContext: Element | null = null): Promise<void> {
private async inject(
browsingContext: WdioElement | null = null
): Promise<void> {
await this.setBrowsingContext(browsingContext);
const runPartialSupported = await axeSourceInject(
this.client,
Expand Down Expand Up @@ -246,15 +253,15 @@ export default class AxeBuilder {
// ensure we fail quickly if an iframe cannot be loaded (instead of waiting
// the default length of 30 seconds)
const { pageLoad } = await this.client.getTimeouts();
this.client.setTimeout({
(this.client as WebdriverIO.Browser).setTimeout({
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This fixes a problem with older versions of webdriverio as in newer versions they declare the function with the first parameter as this: Webdriverio.Browser.

pageLoad: FRAME_LOAD_TIMEOUT
});

let partials: PartialResults | null;
try {
partials = await this.runPartialRecursive(context);
} finally {
this.client.setTimeout({
(this.client as WebdriverIO.Browser).setTimeout({
pageLoad
});
}
Expand Down Expand Up @@ -301,7 +308,7 @@ export default class AxeBuilder {
* - https://webdriver.io/docs/api/webdriver.html#switchtoframe
*/
private async setBrowsingContext(
id: null | Element | Browser = null
id: null | WdioElement | WdioBrowser = null
): Promise<void> {
if (id) {
await this.client.switchToFrame(id);
Expand All @@ -317,7 +324,7 @@ export default class AxeBuilder {

private async runPartialRecursive(
context: SerialContextObject,
frameStack: Element[] = []
frameStack: WdioElement[] = []
): Promise<PartialResults> {
const frameContexts = await axeGetFrameContext(this.client, context);
const partials: PartialResults = [
Expand Down Expand Up @@ -362,7 +369,7 @@ export default class AxeBuilder {

try {
await client.switchToWindow(newWindow.handle);
await client.url('about:blank');
await (client as WebdriverIO.Browser).url('about:blank');
} catch (error) {
throw new Error(
`switchToWindow failed. Are you using updated browser drivers? \nDriver reported:\n${
Expand Down
33 changes: 31 additions & 2 deletions packages/webdriverio/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,43 @@
import type { Browser } from 'webdriverio';
import type { AxeResults, BaseSelector } from 'axe-core';
import * as axe from 'axe-core';
import { type Browser, type Element } from 'webdriverio';

/*
This type allows both webdriverio v8 and <=v7 Browser types
to work in the same codebase. The types are incompatible with
each other, but are compatible with the functions that we use.
Every new feature that we use from the Browser type will need
to be added to the Pick list
*/
export type WdioBrowser =
| Browser
| Pick<
WebdriverIO.Browser,
| '$$'
| '$'
| 'switchToFrame'
| 'switchToParentFrame'
| 'getWindowHandles'
| 'getWindowHandle'
| 'switchToWindow'
| 'createWindow'
| 'url'
| 'getTimeouts'
| 'setTimeout'
| 'closeWindow'
| 'executeAsync'
| 'execute'
>;

export type WdioElement = Element | WebdriverIO.Element;

export type CallbackFunction = (
error: string | null,
results: AxeResults | null
) => void;

export interface Options {
client: Browser;
client: WdioBrowser;
axeSource?: string;
}

Expand Down
44 changes: 22 additions & 22 deletions packages/webdriverio/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import assert from 'assert';
import type { Browser } from 'webdriverio';
import type {
AxeResults,
PartialResult,
Expand All @@ -9,13 +8,14 @@ import type {
SerialSelectorList,
SerialContextObject
} from 'axe-core';
import type { WdioBrowser } from './types';

export const FRAME_LOAD_TIMEOUT = 1000;

/**
* Validates that the client provided is WebdriverIO v5+.
*/
export const isWebdriverClient = (client: Browser): boolean => {
export const isWebdriverClient = (client: WdioBrowser): boolean => {
if (!client) {
return false;
}
Expand Down Expand Up @@ -82,14 +82,14 @@ const promisify = <T>(thenable: Promise<T>): Promise<T> => {
};

export const axeSourceInject = async (
client: Browser,
client: WdioBrowser,
axeSource: string
): Promise<{ runPartialSupported: boolean }> => {
await assertFrameReady(client);
return promisify(
// Had to use executeAsync() because we could not use multiline statements in client.execute()
// we were able to return a single boolean in a line but not when assigned to a variable.
client.executeAsync(`
(client as WebdriverIO.Browser).executeAsync(`
var callback = arguments[arguments.length - 1];
${axeSource};
window.axe.configure({
Expand All @@ -101,7 +101,7 @@ export const axeSourceInject = async (
);
};

async function assertFrameReady(client: Browser): Promise<void> {
async function assertFrameReady(client: WdioBrowser): Promise<void> {
// Wait so that we know there is an execution context.
// Assume that if we have an html node we have an execution context.
try {
Expand All @@ -119,7 +119,7 @@ async function assertFrameReady(client: Browser): Promise<void> {
reject();
}, FRAME_LOAD_TIMEOUT);
});
const executePromise = client.execute(() => {
const executePromise = (client as WebdriverIO.Browser).execute(() => {
return document.readyState === 'complete';
});
const readyState = await Promise.race([timeoutPromise, executePromise]);
Expand All @@ -130,13 +130,13 @@ async function assertFrameReady(client: Browser): Promise<void> {
}

export const axeRunPartial = (
client: Browser,
client: WdioBrowser,
context?: SerialContextObject,
options?: RunOptions
): Promise<PartialResult> => {
return promisify(
client
.executeAsync<string, []>(
Copy link
Contributor Author

@straker straker Dec 21, 2023

Choose a reason for hiding this comment

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

Older versions of webdriverio don't declare the type of the return so we can't declare it either. This defaults to <unknown, []> for the type in newer versions which is why we need to make the change below to make the return type unknown and then cast it to a string

(client as WebdriverIO.Browser)
.executeAsync(
`
var callback = arguments[arguments.length - 1];
var context = ${JSON.stringify(context)} || document;
Expand All @@ -145,20 +145,20 @@ export const axeRunPartial = (
callback(JSON.stringify(partials))
});`
)
.then((r: string) => deserialize<PartialResult>(r))
.then(r => deserialize<PartialResult>(r as string))
);
};

export const axeGetFrameContext = (
client: Browser,
client: WdioBrowser,
context: SerialContextObject
// TODO: add proper types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): Promise<any[]> => {
return promisify(
// Had to use executeAsync() because we could not use multiline statements in client.execute()
// we were able to return a single boolean in a line but not when assigned to a variable.
client.executeAsync(`
(client as WebdriverIO.Browser).executeAsync(`
var callback = arguments[arguments.length - 1];
var context = ${JSON.stringify(context)};
var frameContexts = window.axe.utils.getFrameContexts(context);
Expand All @@ -168,14 +168,14 @@ export const axeGetFrameContext = (
};

export const axeRunLegacy = (
client: Browser,
client: WdioBrowser,
context: SerialContextObject,
options: RunOptions,
config?: Spec
): Promise<AxeResults> => {
return promisify(
client
.executeAsync<string, []>(
(client as WebdriverIO.Browser)
.executeAsync(
`var callback = arguments[arguments.length - 1];
var context = ${JSON.stringify(context)} || document;
var options = ${JSON.stringify(options)} || {};
Expand All @@ -187,12 +187,12 @@ export const axeRunLegacy = (
callback(JSON.stringify(axeResults))
});`
)
.then((r: string) => deserialize<AxeResults>(r))
.then(r => deserialize<AxeResults>(r as string))
);
};

export const axeFinishRun = (
client: Browser,
client: WdioBrowser,
axeSource: string,
partialResults: PartialResults,
options: RunOptions
Expand All @@ -207,7 +207,7 @@ export const axeFinishRun = (
function chunkResults(result: string): Promise<void> {
const chunk = JSON.stringify(result.substring(0, sizeLimit));
return promisify(
client.execute(
(client as WebdriverIO.Browser).execute(
`
window.partialResults ??= '';
window.partialResults += ${chunk};
Expand All @@ -223,7 +223,7 @@ export const axeFinishRun = (
return chunkResults(partialString)
.then(() => {
return promisify(
client.executeAsync<string, []>(
(client as WebdriverIO.Browser).executeAsync(
`var callback = arguments[arguments.length - 1];
${axeSource};
window.axe.configure({
Expand All @@ -238,12 +238,12 @@ export const axeFinishRun = (
)
);
})
.then((r: string) => deserialize<AxeResults>(r));
.then(r => deserialize<AxeResults>(r as string));
};

export const configureAllowedOrigins = (client: Browser): Promise<void> => {
export const configureAllowedOrigins = (client: WdioBrowser): Promise<void> => {
return promisify(
client.execute(`
(client as WebdriverIO.Browser).execute(`
window.axe.configure({ allowedOrigins: ['<unsafe_all_origins>'] })
`)
);
Expand Down
2 changes: 1 addition & 1 deletion packages/webdriverio/test/axe-webdriverio.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('@axe-core/webdriverio', () => {
describe(`WebdriverIO Async (${protocol} protocol)`, () => {
let server: Server;
let addr: string;
let client: webdriverio.Browser;
let client: WebdriverIO.Browser;
const axePath = require.resolve('axe-core');
const axeSource = fs.readFileSync(axePath, 'utf8');
const axeLegacySource = fs.readFileSync(
Expand Down
3 changes: 3 additions & 0 deletions packages/webdriverio/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["node", "mocha", "@wdio/globals/types"]
},
"include": ["src"],
"exclude": ["test"]
}
10 changes: 5 additions & 5 deletions test/wdio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
"@axe-core/webdriverio": "^4.8.2",
"@types/chai": "^4.3.9",
"@types/mocha": "^10.0.3",
"@wdio/cli": "^8.20.2",
"@wdio/globals": "^8.20.0",
"@wdio/local-runner": "^8.20.0",
"@wdio/mocha-framework": "^8.20.0",
"@wdio/spec-reporter": "^8.20.0",
"@wdio/cli": "^8.27.0",
"@wdio/globals": "^8.27.0",
"@wdio/local-runner": "^8.27.0",
"@wdio/mocha-framework": "^8.27.0",
"@wdio/spec-reporter": "^8.27.0",
"async-listen": "^3.0.1",
"axe-test-fixtures": "github:dequelabs/axe-test-fixtures#v1",
"chai": "^4.3.10",
Expand Down