Skip to content

Commit b72c735

Browse files
scottmriesZidiousmichael-siek
authored
fix: get correct new window handle with Selenium 3 workaround (#1031)
This is the workaround fix that supports Selenium 3 and 4. We would likely switch to #1027 should we drop Selenium 3 support. Rather than always using the last handle after opening a new window, we filter for the unique new handle and use it. This is analogous to the [axe-core-maven-html approach](https://github.com/dequelabs/axe-core-maven-html/blob/ad58b26a8d0e2f1afed33b2c5cbca22b54644b99/selenium/src/main/java/com/deque/html/axecore/extensions/WebDriverExtensions.java#L126). Closes: #936 --------- Co-authored-by: Zidious <[email protected]> Co-authored-by: michael-siek <[email protected]>
1 parent 7e152b6 commit b72c735

File tree

3 files changed

+98
-4
lines changed

3 files changed

+98
-4
lines changed

packages/webdriverjs/src/index.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,21 @@ export default class AxeBuilder {
274274
await driver.switchTo().window(win);
275275

276276
try {
277-
await driver.executeScript(`window.open('about:blank')`);
278-
const handlers = await driver.getAllWindowHandles();
279-
await driver.switchTo().window(handlers[handlers.length - 1]);
277+
// This is a workaround to maintain support for Selenium 3, which does not have the `newWindow` API in Selenium 4.
278+
// TODO: Remove this workaround should we drop support for Selenium 3
279+
// https://github.com/dequelabs/axe-core-npm/issues/1032
280+
const beforeHandles = await driver.getAllWindowHandles();
281+
await driver.executeScript(`window.open('about:blank', '_blank')`);
282+
const afterHandles = await driver.getAllWindowHandles();
283+
const newHandles = afterHandles.filter(
284+
afterHandle => beforeHandles.indexOf(afterHandle) === -1
285+
);
286+
287+
if (newHandles.length !== 1) {
288+
throw new Error('Unable to determine window handle');
289+
}
290+
const newHandle = newHandles[0];
291+
await driver.switchTo().window(newHandle);
280292
await driver.get('about:blank');
281293
} catch (error) {
282294
throw new Error(

packages/webdriverjs/test/axe-webdriverjs.spec.ts

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { assert } from 'chai';
77
import path from 'path';
88
import fs from 'fs';
99
import { Server, createServer } from 'http';
10-
import { Webdriver } from './test-utils';
10+
import { FirefoxDriver, SafariDriver, Webdriver } from './test-utils';
1111
import { AxeBuilder } from '../src';
1212
import { axeRunPartial } from '../src/browser';
1313
import { fixturesPath } from 'axe-test-fixtures';
14+
import { Command, Name } from 'selenium-webdriver/lib/command';
1415

1516
const dylangConfig = JSON.parse(
1617
fs.readFileSync(path.join(fixturesPath, 'dylang-config.json'), 'utf8')
@@ -837,6 +838,48 @@ describe('@axe-core/webdriverjs', () => {
837838
);
838839
}
839840
});
841+
it('throws an error if switchTo fails', async () => {
842+
driver.switchTo = () => {
843+
throw new Error('switchTo failed.');
844+
};
845+
const title = await driver.getTitle();
846+
847+
assert.notEqual(title, 'Error');
848+
try {
849+
await new AxeBuilder(driver, axeSource).analyze();
850+
assert.fail('Should have thrown');
851+
} catch (err) {
852+
assert.match((err as Error).message, /switchTo failed./);
853+
}
854+
});
855+
it('throws an error if unable to determine window handle', async () => {
856+
// note: overriding executeScript to run twice and thus force finishRun to throw
857+
driver.executeScript = async (script, ...args) => {
858+
driver.execute(
859+
new Command(Name.EXECUTE_SCRIPT)
860+
.setParameter('script', script)
861+
.setParameter('args', args)
862+
);
863+
return driver.execute(
864+
new Command(Name.EXECUTE_SCRIPT)
865+
.setParameter('script', script)
866+
.setParameter('args', args)
867+
);
868+
};
869+
const title = await driver.getTitle();
870+
871+
assert.notEqual(title, 'Error');
872+
try {
873+
await new AxeBuilder(driver, axeSource).analyze();
874+
assert.fail('Should have thrown');
875+
} catch (err) {
876+
assert.match((err as Error).message, /Unable to determine window/);
877+
assert.include(
878+
(err as Error).message,
879+
'Please check out https://github.com/dequelabs/axe-core-npm/blob/develop/packages/webdriverjs/error-handling.md'
880+
);
881+
}
882+
});
840883
});
841884

842885
describe('setLegacyMode', () => {
@@ -1026,4 +1069,31 @@ describe('@axe-core/webdriverjs', () => {
10261069
assert.lengthOf(allowedOrigins, 1);
10271070
});
10281071
});
1072+
1073+
describe('driver', () => {
1074+
it(`should not throw when it's Chrome`, async () => {
1075+
assert.doesNotThrow(async () => {
1076+
await driver.get(`${addr}/index.html`);
1077+
await driver.get('about:blank');
1078+
}, Error);
1079+
});
1080+
it(`should not throw when it's Firefox`, async () => {
1081+
driver = FirefoxDriver();
1082+
assert.doesNotThrow(async () => {
1083+
await driver.get(`${addr}/index.html`);
1084+
await driver.get('about:blank');
1085+
}, Error);
1086+
});
1087+
// skip this if we're not on Mac
1088+
(process.platform === 'darwin' ? it : it.skip)(
1089+
`should not throw when it's Safari`,
1090+
async () => {
1091+
driver = SafariDriver();
1092+
assert.doesNotThrow(async () => {
1093+
await driver.get(`${addr}/index.html`);
1094+
await driver.get('about:blank');
1095+
}, Error);
1096+
}
1097+
);
1098+
});
10291099
});

packages/webdriverjs/test/test-utils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { WebDriver, Builder } from 'selenium-webdriver';
22
import chromedriver from 'chromedriver';
33
import chrome from 'selenium-webdriver/chrome';
4+
import firefox from 'selenium-webdriver/firefox';
45

56
export const Webdriver = (): WebDriver => {
67
const builder = new Builder()
@@ -18,3 +19,14 @@ export const Webdriver = (): WebDriver => {
1819

1920
return builder.build();
2021
};
22+
23+
export const FirefoxDriver = (): WebDriver => {
24+
return new Builder()
25+
.forBrowser('firefox')
26+
.setFirefoxOptions(new firefox.Options().addArguments('--headless'))
27+
.build();
28+
};
29+
30+
export const SafariDriver = (): WebDriver => {
31+
return new Builder().forBrowser('safari').build();
32+
};

0 commit comments

Comments
 (0)