mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-28 10:07:59 -05:00
test: simplify SeleniumHelperError
This commit is contained in:
parent
4fb4eaab76
commit
8cf09452c6
1 changed files with 62 additions and 112 deletions
|
@ -37,58 +37,10 @@ Error.prepareStackTrace = function (error, stack) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An error that can be chained to another error.
|
* An error thrown by SeleniumHelper.
|
||||||
* @extends Error
|
* @extends Error
|
||||||
*/
|
*/
|
||||||
class ChainableError extends Error {
|
class SeleniumHelperError extends Error {
|
||||||
/**
|
|
||||||
* Instantiate a new ChainableError.
|
|
||||||
* @param {string} message The error message for this layer.
|
|
||||||
*/
|
|
||||||
constructor (message) {
|
|
||||||
super(message);
|
|
||||||
this.baseMessage = message;
|
|
||||||
Object.setPrototypeOf(this, ChainableError.prototype); // see https://stackoverflow.com/a/41102306
|
|
||||||
this.name = 'ChainableError';
|
|
||||||
Error.captureStackTrace(this, this.constructor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new layer to the error chain.
|
|
||||||
* @param {Error} innerError The error to add to the chain.
|
|
||||||
* @returns {ChainableError} This error, with the new layer added.
|
|
||||||
*/
|
|
||||||
chain (innerError) {
|
|
||||||
this.innerError = innerError;
|
|
||||||
|
|
||||||
const messages = [];
|
|
||||||
this.collectMessages(messages);
|
|
||||||
this.message = messages.join('\n');
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collect all the error messages in the chain.
|
|
||||||
* @param {Array<string>} lines The array to collect the messages into.
|
|
||||||
*/
|
|
||||||
collectMessages (lines) {
|
|
||||||
lines.push(this.baseMessage);
|
|
||||||
if (this.innerError) {
|
|
||||||
if (this.innerError.collectMessages) {
|
|
||||||
this.innerError.collectMessages(lines);
|
|
||||||
} else {
|
|
||||||
lines.push(this.innerError.baseMessage || this.innerError.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An error thrown by SeleniumHelper.
|
|
||||||
* @extends ChainableError
|
|
||||||
*/
|
|
||||||
class SeleniumHelperError extends ChainableError {
|
|
||||||
/**
|
/**
|
||||||
* Instantiate a new SeleniumHelperError.
|
* Instantiate a new SeleniumHelperError.
|
||||||
* @param {string} message The error message for this layer.
|
* @param {string} message The error message for this layer.
|
||||||
|
@ -98,8 +50,7 @@ class SeleniumHelperError extends ChainableError {
|
||||||
* try {
|
* try {
|
||||||
* doThings();
|
* doThings();
|
||||||
* } catch (inner) {
|
* } catch (inner) {
|
||||||
* await e.collectContext(driver);
|
* throw await e.chain(inner, driver);
|
||||||
* throw e.chain(inner);
|
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
constructor (message, kvList = []) {
|
constructor (message, kvList = []) {
|
||||||
|
@ -114,44 +65,57 @@ class SeleniumHelperError extends ChainableError {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect error context from the webdriver.
|
* Add a new layer to the error chain.
|
||||||
* @param {webdriver.ThenableWebDriver} driver The webdriver instance.
|
* Collects context from the webdriver if it is present AND this is the innermost `SeleniumHelperError`.
|
||||||
* @returns {Promise} A promise that resolves when the context is collected.
|
* @param {Error|SeleniumHelperError} innerError The error to add to the chain.
|
||||||
|
* @param {webdriver.ThenableWebDriver} [driver] Optional webdriver instance to collect context from.
|
||||||
|
* @returns {Promise<SeleniumHelperError>} This error, with the new layer added.
|
||||||
*/
|
*/
|
||||||
async collectContext (driver) {
|
async chain (innerError, driver) {
|
||||||
// It would be really nice to wait until `message` time to collect all this information,
|
const messageLines = [
|
||||||
// but that's not an option because of all these async calls.
|
this.message,
|
||||||
const url = await driver.getCurrentUrl();
|
innerError.message
|
||||||
const title = await driver.getTitle();
|
|
||||||
// const pageSource = await driver.getPageSource();
|
|
||||||
const browserLogEntries = await driver.manage()
|
|
||||||
.logs()
|
|
||||||
.get('browser');
|
|
||||||
const browserLogText = browserLogEntries.map(entry => entry.message).join('\n');
|
|
||||||
this.contextLines = [
|
|
||||||
`Browser URL: ${url}`,
|
|
||||||
`Browser title: ${title}`,
|
|
||||||
`Browser logs:\n*****\n${browserLogText}\n*****`
|
|
||||||
// `Browser page source:\n*****\n${pageSource}\n*****`
|
|
||||||
];
|
];
|
||||||
}
|
// If the inner error has already collected context, don't collect it again.
|
||||||
|
if (driver && !(innerError && innerError.collectContext)) {
|
||||||
chain (innerError) {
|
await this.collectContext(messageLines, driver);
|
||||||
super.chain(innerError); // rebuilds the message
|
|
||||||
let messageLines = [
|
|
||||||
this.message
|
|
||||||
];
|
|
||||||
if (this.innerError && this.innerError.contextLines) {
|
|
||||||
this.contextLines = this.innerError.contextLines;
|
|
||||||
}
|
|
||||||
if (this.contextLines) {
|
|
||||||
messageLines = messageLines.concat(this.contextLines);
|
|
||||||
} else {
|
|
||||||
messageLines.push('(no webdriver context collected)');
|
|
||||||
}
|
}
|
||||||
this.message = messageLines.join('\n');
|
this.message = messageLines.join('\n');
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect error context from the webdriver.
|
||||||
|
* @param {Array<string>} messageLines Add context lines to this array.
|
||||||
|
* @param {webdriver.ThenableWebDriver} driver The webdriver instance to collect context from.
|
||||||
|
* @returns {Promise} A promise that resolves when the context is collected.
|
||||||
|
*/
|
||||||
|
async collectContext (messageLines, driver) {
|
||||||
|
// It would be really nice to wait until `message` time to collect all this information,
|
||||||
|
// but that's not an option because of all these async calls.
|
||||||
|
const [
|
||||||
|
url,
|
||||||
|
title,
|
||||||
|
// pageSource,
|
||||||
|
logEntries
|
||||||
|
] = await Promise.all([
|
||||||
|
driver.getCurrentUrl(),
|
||||||
|
driver.getTitle(),
|
||||||
|
// driver.getPageSource(),
|
||||||
|
driver.manage()
|
||||||
|
.logs()
|
||||||
|
.get('browser')
|
||||||
|
]);
|
||||||
|
messageLines.push(
|
||||||
|
`Browser URL: ${url}`,
|
||||||
|
`Browser title: ${title}`,
|
||||||
|
'Browser logs:',
|
||||||
|
'*****',
|
||||||
|
...logEntries.map(entry => entry.message),
|
||||||
|
'*****'
|
||||||
|
// 'Browser page source:', '*****', pageSource, '*****'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SeleniumHelper {
|
class SeleniumHelper {
|
||||||
|
@ -295,8 +259,7 @@ class SeleniumHelper {
|
||||||
await this.driver.wait(el.isDisplayed(), DEFAULT_TIMEOUT_MILLISECONDS);
|
await this.driver.wait(el.isDisplayed(), DEFAULT_TIMEOUT_MILLISECONDS);
|
||||||
return el;
|
return el;
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
await outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,8 +272,7 @@ class SeleniumHelper {
|
||||||
try {
|
try {
|
||||||
await this.driver.wait(until.stalenessOf(element), DEFAULT_TIMEOUT_MILLISECONDS);
|
await this.driver.wait(until.stalenessOf(element), DEFAULT_TIMEOUT_MILLISECONDS);
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
await outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,8 +287,7 @@ class SeleniumHelper {
|
||||||
const el = await this.findByXpath(xpath);
|
const el = await this.findByXpath(xpath);
|
||||||
await el.click();
|
await el.click();
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,8 +301,7 @@ class SeleniumHelper {
|
||||||
try {
|
try {
|
||||||
await this.clickXpath(`//*[contains(text(), '${text}')]`);
|
await this.clickXpath(`//*[contains(text(), '${text}')]`);
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,8 +318,7 @@ class SeleniumHelper {
|
||||||
DEFAULT_TIMEOUT_MILLISECONDS
|
DEFAULT_TIMEOUT_MILLISECONDS
|
||||||
);
|
);
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,8 +332,7 @@ class SeleniumHelper {
|
||||||
try {
|
try {
|
||||||
await this.clickXpath(`//button[contains(text(), '${text}')]`);
|
await this.clickXpath(`//button[contains(text(), '${text}')]`);
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,8 +346,7 @@ class SeleniumHelper {
|
||||||
try {
|
try {
|
||||||
return await this.driver.wait(until.elementLocated(By.css(css)), DEFAULT_TIMEOUT_MILLISECONDS);
|
return await this.driver.wait(until.elementLocated(By.css(css)), DEFAULT_TIMEOUT_MILLISECONDS);
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,8 +361,7 @@ class SeleniumHelper {
|
||||||
const el = await this.findByCss(css);
|
const el = await this.findByCss(css);
|
||||||
await el.click();
|
await el.click();
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,8 +380,7 @@ class SeleniumHelper {
|
||||||
.dragAndDrop(startEl, endEl)
|
.dragAndDrop(startEl, endEl)
|
||||||
.perform();
|
.perform();
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,8 +403,7 @@ class SeleniumHelper {
|
||||||
await word.sendKeys(password + this.getKey('ENTER'));
|
await word.sendKeys(password + this.getKey('ENTER'));
|
||||||
await this.findByXpath('//span[contains(@class, "profile-name")]');
|
await this.findByXpath('//span[contains(@class, "profile-name")]');
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,8 +417,7 @@ class SeleniumHelper {
|
||||||
try {
|
try {
|
||||||
await this.driver.wait(until.urlMatches(regex), DEFAULT_TIMEOUT_MILLISECONDS);
|
await this.driver.wait(until.urlMatches(regex), DEFAULT_TIMEOUT_MILLISECONDS);
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,8 +449,7 @@ class SeleniumHelper {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,8 +466,7 @@ class SeleniumHelper {
|
||||||
const classList = classes.split(' ');
|
const classList = classes.split(' ');
|
||||||
return classList.includes(cl);
|
return classList.includes(cl);
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(this.driver);
|
throw await outerError.chain(cause, this.driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,8 +480,7 @@ class SeleniumHelper {
|
||||||
try {
|
try {
|
||||||
await driver.wait(until.elementIsVisible(element), DEFAULT_TIMEOUT_MILLISECONDS);
|
await driver.wait(until.elementIsVisible(element), DEFAULT_TIMEOUT_MILLISECONDS);
|
||||||
} catch (cause) {
|
} catch (cause) {
|
||||||
outerError.collectContext(driver);
|
throw await outerError.chain(cause, driver);
|
||||||
throw outerError.chain(cause);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue