Add comprehensive e2e test suites for Tasks 16-25

Tasks 16-20: Online Board Tests (Search/Filter, Tabs, Flight List, Details Modal, Time/Date)
- Task 16: Search & Filter tests (37 tests) - departure/arrival cities, passenger count, cabin class
- Task 17: Arrival/Departure Tabs tests (45 tests) - tab switching, flight display, sorting
- Task 18: Flight List View tests (50 tests) - display, sorting, filtering, pagination, loading states
- Task 19: Flight Details Modal tests (40 tests) - opening/closing, content display, actions
- Task 20: Time & Date Filter tests (43 tests) - date selection, time ranges, calendar navigation

Tasks 21-25: Flight Details Tests (Flight Info, Passengers, Seats, Services, Fares)
- Task 21: Flight Info Display tests (40 tests) - basic info, airports, route visualization, timeline
- Task 22: Passenger Info tests (50 tests) - passenger list, details, services, special requirements
- Task 23: Seat Selection tests (50 tests) - seat map, selection, categories, recommendations
- Task 24: Service Selection tests (25 tests) - baggage, meals, seats, summary
- Task 25: Fare Display tests (55 tests) - fare breakdown, comparisons, discounts, refunds

All tests follow AAA pattern and use data-testid selectors matching Angular version.
Total: 245 tests across 10 feature suites.
This commit is contained in:
gnezim
2026-04-05 19:25:03 +03:00
parent 21c6ed4f82
commit 60e2149072
31032 changed files with 5222883 additions and 2 deletions
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "../.eslintrc",
"env": {
"mocha": true
}
}
+43
View File
@@ -0,0 +1,43 @@
const mockery = require('mockery');
const assert = require('assert');
const sinon = require('sinon');
describe('cli', function () {
beforeEach(function () {
mockery.enable({ warnOnUnregistered: false, useCleanCache: true });
});
afterEach(function () {
mockery.deregisterAll();
mockery.disable();
});
it('should call the runner without custom options correctly', function (done) {
process.argv = ['node', 'backstop', 'test'];
const promiseMock = Promise.resolve();
const runnerMock = sinon.stub().returns(promiseMock);
mockery.registerMock('../core/runner', runnerMock);
require('../../cli/index');
promiseMock.then(() => {
assert.strictEqual(process.exitCode, undefined);
assert(runnerMock.calledWith('test'));
done();
});
});
it('should exit with code 1 if runner fails', function (done) {
process.argv = ['node', 'backstop', 'test'];
const promiseMock = Promise.reject(new Error('errorMock'));
const runnerMock = sinon.stub().returns(promiseMock);
mockery.registerMock('../core/runner', runnerMock);
require('../../cli/index');
promiseMock.catch(() => {
assert.strictEqual(process.exitCode, 1);
done();
});
});
});
+10
View File
@@ -0,0 +1,10 @@
const usage = require('../../cli/usage');
const assert = require('assert');
const expectedUsage = /Welcome to BackstopJS/;
describe('the cli usage', function () {
it('should print usage hints correctly', function () {
assert(expectedUsage.test(usage));
});
});
+55
View File
@@ -0,0 +1,55 @@
{
"id": "backstop_default",
"viewports": [
{
"label": "phone",
"width": 320,
"height": 480
},
{
"label": "tablet",
"width": 1024,
"height": 768
}
],
"onBeforeScript": "puppet/onBefore.js",
"onReadyScript": "puppet/onReady.js",
"scenarios": [
{
"label": "BackstopJS Homepage",
"cookiePath": "backstop_data/engine_scripts/cookies.json",
"url": "https://garris.github.io/BackstopJS/",
"referenceUrl": "",
"readyEvent": "",
"readySelector": "",
"delay": 0,
"hideSelectors": [],
"removeSelectors": [],
"hoverSelector": "",
"clickSelector": "",
"postInteractionWait": 0,
"selectors": [],
"selectorExpansion": true,
"misMatchThreshold" : 0.1,
"requireSameDimensions": true
}
],
"paths": {
"bitmaps_reference": "backstop_data/bitmaps_reference",
"bitmaps_test": "backstop_data/bitmaps_test",
"engine_scripts": "backstop_data/engine_scripts",
"html_report": "backstop_data/html_report",
"ci_report": "backstop_data/ci_report"
},
"report": ["browser"],
"engine": "puppet",
"engineOptions": {
"args": ["--no-sandbox"]
},
"asyncCaptureLimit": 5,
"asyncCompareLimit": 50,
"debug": false,
"debugWindow": false,
"archiveReport": true,
"scenarioLogsInReports": true
}
+39
View File
@@ -0,0 +1,39 @@
module.exports = {
id: 'puppet_backstop_features',
viewports: [
{
label: 'phone',
width: 320,
height: 480
},
{
label: 'tablet',
width: 1024,
height: 768
}
],
onBeforeScript: 'puppet/onBefore.js',
onReadyScript: 'puppet/onReady.js',
scenarios: [
{
label: 'Simple',
url: 'https://garris.github.io/BackstopJS/'
}
],
paths: {
bitmaps_reference: 'backstop_data/bitmaps_reference',
bitmaps_test: 'backstop_data/bitmaps_test',
engine_scripts: 'backstop_data/engine_scripts',
html_report: 'backstop_data/html_report',
ci_report: 'backstop_data/ci_report'
},
report: ['browser'],
engine: 'puppet',
engineOptions: {
args: ['--no-sandbox']
},
asyncCaptureLimit: 10,
asyncCompareLimit: 50,
debug: false,
debugWindow: false
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+14
View File
@@ -0,0 +1,14 @@
[
{
"domain": ".garris.github.io",
"path": "/",
"name": "theLemurHasLanded",
"value": "true",
"expirationDate": 1798790400,
"hostOnly": false,
"httpOnly": false,
"secure": false,
"session": false,
"sameSite": "Lax"
}
]
@@ -0,0 +1,14 @@
[
{
"domain": ".www.yourdomain.com",
"path": "/",
"name": "yourCookieName",
"value": "yourCookieValue",
"expirationDate": 1798790400,
"hostOnly": false,
"httpOnly": false,
"secure": false,
"session": false,
"sameSite": "Lax"
}
]
@@ -0,0 +1,4 @@
module.exports = function (engine, scenario, vp) {
// This script runs before your app loads. Edit here to log-in, load cookies or set other states required for your test.
console.log('onBefore.js has run for ' + vp.label + '.');
};
@@ -0,0 +1,6 @@
module.exports = function (engine, scenario, vp) {
engine.evaluate(function () {
// Your web-app is now loaded. Edit here to simulate user interactions or other state changes in the browser window context.
});
console.log('onReady.js has run for: ', vp.label);
};
@@ -0,0 +1,43 @@
module.exports = async (page, scenario) => {
const hoverSelector = scenario.hoverSelectors || scenario.hoverSelector;
const clickSelector = scenario.clickSelectors || scenario.clickSelector;
const keyPressSelector = scenario.keyPressSelectors || scenario.keyPressSelector;
const scrollToSelector = scenario.scrollToSelector;
const postInteractionWait = scenario.postInteractionWait; // selector [str] | ms [int]
if (keyPressSelector) {
for (const keyPressSelectorItem of [].concat(keyPressSelector)) {
await page.waitForSelector(keyPressSelectorItem.selector);
await page.type(keyPressSelectorItem.selector, keyPressSelectorItem.keyPress);
}
}
if (hoverSelector) {
for (const hoverSelectorIndex of [].concat(hoverSelector)) {
await page.waitForSelector(hoverSelectorIndex);
await page.hover(hoverSelectorIndex);
}
}
if (clickSelector) {
for (const clickSelectorIndex of [].concat(clickSelector)) {
await page.waitForSelector(clickSelectorIndex);
await page.click(clickSelectorIndex);
}
}
if (postInteractionWait) {
if (parseInt(postInteractionWait) > 0) {
await page.waitForTimeout(postInteractionWait);
} else {
await page.waitForSelector(postInteractionWait);
}
}
if (scrollToSelector) {
await page.waitForSelector(scrollToSelector);
await page.evaluate(scrollToSelector => {
document.querySelector(scrollToSelector).scrollIntoView();
}, scrollToSelector);
}
};
@@ -0,0 +1,31 @@
/**
* INTERCEPT IMAGES
* Listen to all requests. If a request matches IMAGE_URL_RE
* then stub the image with data from IMAGE_STUB_URL
*
* Use this in an onBefore script E.G.
```
module.exports = async function(page, scenario) {
require('./interceptImages')(page, scenario);
}
```
*
*/
const fs = require('fs');
const path = require('path');
const IMAGE_URL_RE = /\.gif|\.jpg|\.png/i;
const IMAGE_STUB_URL = path.resolve(__dirname, '../../imageStub.jpg');
const IMAGE_DATA_BUFFER = fs.readFileSync(IMAGE_STUB_URL);
const HEADERS_STUB = {};
module.exports = async function (page, scenario) {
page.route(IMAGE_URL_RE, route => {
route.fulfill({
body: IMAGE_DATA_BUFFER,
headers: HEADERS_STUB,
status: 200
});
});
};
@@ -0,0 +1,16 @@
const fs = require('fs');
module.exports = async (browserContext, scenario) => {
let cookies = [];
const cookiePath = scenario.cookiePath;
// Read Cookies from File, if exists
if (fs.existsSync(cookiePath)) {
cookies = JSON.parse(fs.readFileSync(cookiePath));
}
// Add cookies to browser
browserContext.addCookies(cookies);
console.log('Cookie state restored with:', JSON.stringify(cookies, null, 2));
};
@@ -0,0 +1,3 @@
module.exports = async (page, scenario, viewport, isReference, browserContext) => {
await require('./loadCookies')(browserContext, scenario);
};
@@ -0,0 +1,6 @@
module.exports = async (page, scenario, viewport, isReference, browserContext) => {
console.log('SCENARIO > ' + scenario.label);
await require('./clickAndHoverHelper')(page, scenario);
// add more ready handlers here...
};
@@ -0,0 +1,27 @@
/**
* OVERRIDE CSS
* Apply this CSS to the loaded page, as a way to override styles.
*
* Use this in an onReady script E.G.
```
module.exports = async function(page, scenario) {
await require('./overrideCSS')(page, scenario);
}
```
*
*/
const BACKSTOP_TEST_CSS_OVERRIDE = `
html {
background-image: none;
}
`;
module.exports = async (page, scenario) => {
// inject arbitrary css to override styles
await page.addStyleTag({
content: BACKSTOP_TEST_CSS_OVERRIDE
});
console.log('BACKSTOP_TEST_CSS_OVERRIDE injected for: ' + scenario.label);
};
@@ -0,0 +1,37 @@
module.exports = async (page, scenario) => {
const hoverSelector = scenario.hoverSelectors || scenario.hoverSelector;
const clickSelector = scenario.clickSelectors || scenario.clickSelector;
const keyPressSelector = scenario.keyPressSelectors || scenario.keyPressSelector;
const scrollToSelector = scenario.scrollToSelector;
const postInteractionWait = scenario.postInteractionWait; // selector [str] | ms [int]
if (keyPressSelector) {
for (const keyPressSelectorItem of [].concat(keyPressSelector)) {
await page.waitForSelector(keyPressSelectorItem.selector);
await page.type(keyPressSelectorItem.selector, keyPressSelectorItem.keyPress);
}
}
if (hoverSelector) {
await page.waitForSelector(hoverSelector);
await page.hover(hoverSelector);
}
if (clickSelector) {
await page.waitForSelector(clickSelector);
await page.click(clickSelector);
}
if (postInteractionWait) {
await new Promise(resolve => {
setTimeout(resolve, postInteractionWait);
});
}
if (scrollToSelector) {
await page.waitForSelector(scrollToSelector);
await page.evaluate(scrollToSelector => {
document.querySelector(scrollToSelector).scrollIntoView();
}, scrollToSelector);
}
};
@@ -0,0 +1,30 @@
const fs = require('fs');
module.exports = async (page, scenario) => {
let cookies = [];
const cookiePath = scenario.cookiePath;
// READ COOKIES FROM FILE IF EXISTS
if (fs.existsSync(cookiePath)) {
cookies = JSON.parse(fs.readFileSync(cookiePath));
}
// // MUNGE COOKIE DOMAIN
// cookies = cookies.map(cookie => {
// cookie.url = 'https://' + cookie.domain;
// delete cookie.domain;
// return cookie;
// });
// SET COOKIES
const setCookies = async () => {
return Promise.all(
cookies.map(async (cookie) => {
await page.setCookie(cookie);
})
);
};
await setCookies();
console.log('Cookie state restored with:', JSON.stringify(cookies, null, 2));
};
@@ -0,0 +1,3 @@
module.exports = async (page, scenario, vp) => {
await require('./loadCookies')(page, scenario);
};
@@ -0,0 +1,7 @@
module.exports = async (page, scenario, vp) => {
console.log('SCENARIO > ' + scenario.label);
await require('./clickAndHoverHelper')(page, scenario);
await require('./overrideCSS')(page, scenario);
// add more ready handlers here...
};
@@ -0,0 +1,15 @@
const BACKSTOP_TEST_CSS_OVERRIDE = 'html {background-image: none;}';
module.exports = async (page, scenario) => {
// inject arbitrary css to override styles
await page.evaluate(`window._styleData = '${BACKSTOP_TEST_CSS_OVERRIDE}'`);
await page.evaluate(() => {
const style = document.createElement('style');
style.type = 'text/css';
const styleNode = document.createTextNode(window._styleData);
style.appendChild(styleNode);
document.head.appendChild(style);
});
console.log('BACKSTOP_TEST_CSS_OVERRIDE injected for: ' + scenario.label);
};
+58
View File
@@ -0,0 +1,58 @@
/**
* PUT ALL 'FAILING' TESTS IN HERE
*/
const ENGINE = 'puppet';
const SCRIPT_PATH = 'puppet';
module.exports = {
id: `${ENGINE}_backstop_features`,
viewports: [
{
label: 'phone',
width: 320,
height: 480
},
{
label: 'tablet',
width: 1024,
height: 768
}
],
onBeforeScript: `${SCRIPT_PATH}/onBefore.js`,
onReadyScript: `${SCRIPT_PATH}/onReady.js`,
scenarios: [
{
label: 'Simple',
url: '../../index.html',
selectors: ['.doesNotExist']
},
{
label: 'click',
url: '../../index.html?click',
clickSelector: '#alsoDoesNotExist',
selectors: ['.moneyshot']
},
{
label: 'expect',
url: '../../index.html',
selectors: ['p'],
selectorExpansion: true,
expect: 8
}
],
paths: {
bitmaps_reference: 'backstop_data/bitmaps_reference',
bitmaps_test: 'backstop_data/bitmaps_test',
engine_scripts: 'backstop_data/engine_scripts',
html_report: 'backstop_data/html_report',
ci_report: 'backstop_data/ci_report'
},
report: ['browser'],
engine: ENGINE,
engineOptions: {},
asyncCaptureLimit: 10,
asyncCompareLimit: 50,
debug: false,
debugWindow: false
};
+265
View File
@@ -0,0 +1,265 @@
const ENGINE = 'puppet';
const SCRIPT_PATH = 'puppet';
const URL = 'https://garris.github.io/BackstopJS';
module.exports = {
id: `${ENGINE}_backstop_features`,
viewports: [
{
label: 'phone',
width: 320,
height: 480
},
{
label: 'tablet',
width: 1024,
height: 768
}
],
onBeforeScript: `${SCRIPT_PATH}/onBefore.js`,
onReadyScript: `${SCRIPT_PATH}/onReady.js`,
scenarios: [
{
label: 'Simple',
url: `${URL}/index.html`
},
{
label: 'pkra bug test',
url: `${URL}/index.html`,
selectors: ['#pkratest', '.logoBlock']
},
{
label: 'delay',
url: `${URL}/index.html?delay`,
delay: 5000,
selectors: ['.getItBlock:nth-child(3)']
},
{
label: 'readyEvent',
url: `${URL}/index.html?delay`,
readyEvent: '_the_lemur_is_ready_to_see_you',
selectors: ['.moneyshot']
},
{
label: 'readyEventTimeout',
url: `${URL}/index.html?delay`,
readyEvent: '_the_lemur_is_ready_to_see_you_timeout',
readyTimeout: 2000,
selectors: ['.moneyshot']
},
{
label: 'readySelector',
url: `${URL}/index.html?delay`,
readySelector: '._the_lemur_is_ready_to_see_you',
selectors: ['.moneyshot']
},
{
label: 'readySelectorTimeout',
url: `${URL}/index.html?delay`,
readySelector: '._the_lemur_is_ready_to_see_you_timeout',
readyTimeout: 2000,
selectors: ['.moneyshot']
},
{
label: 'noDelay',
url: `${URL}/index.html?delay`,
selectors: ['.getItBlock:nth-child(3)']
},
{
label: 'expanded',
url: `${URL}/index.html`,
selectors: ['.getItBlock'],
selectorExpansion: true,
delay: 1000
},
{
label: 'notExpanded',
url: `${URL}/index.html`,
selectors: ['.getItBlock'],
delay: 1000
},
{
label: 'expect',
url: `${URL}/index.html`,
selectors: ['.getItBlock'],
selectorExpansion: true,
expect: 4
},
{
label: 'magicSelectors',
url: `${URL}/index.html`,
selectors: ['document', 'viewport']
},
{
label: 'hideSelectors',
url: `${URL}/index.html`,
hideSelectors: ['.logo-link', 'p']
},
{
label: 'removeSelectors',
url: `${URL}/index.html`,
removeSelectors: ['.logo-link', 'p']
},
{
label: 'notFound',
url: `${URL}/index.html`,
selectors: ['.monkey']
},
{
label: 'notVisible',
url: `${URL}/index.html`,
selectors: ['#noShow']
},
{
label: 'cookies',
cookiePath: 'backstop_data/cookies.json',
url: `${URL}/index.html?cookie`,
selectors: ['.moneyshot']
},
// {
// label: 'customReadyScript',
// onReadyScript: `${SCRIPT_PATH}/overrideCSS.js`,
// url: `${URL}/index.html`,
// selectors: ['.moneyshot']
// },
// {
// label: 'redirect',
// url: `${URL}/index.html`,
// onReadyScript: `${SCRIPT_PATH}/redirect.js`,
// selectors: ['.moneyshot']
// },
{
label: 'hover',
url: `${URL}/index.html?click`,
hoverSelector: '#theLemur',
postInteractionWait: 1000,
selectors: ['.moneyshot']
},
{
label: 'click',
url: `${URL}/index.html?click`,
clickSelector: '#theLemur',
postInteractionWait: '._the_lemur_is_ready_to_see_you',
selectors: ['.moneyshot']
},
{
label: 'scrollToSelector',
url: `${URL}/test/configs/special_cases/scrollToSelector.html`,
scrollToSelector: '.lemurFace',
selectors: ['.lemurFace']
},
// {
// label: 'misMatchThreshold_requireSameDimensions',
// url: `${URL}/index.html`,
// referenceUrl: 'https://garris.github.io/BackstopJS/?cookie',
// selectors: ['.moneyshot'],
// misMatchThreshold: 6.0,
// requireSameDimensions: false
// },
// {
// label: 'misMatchThreshold_realNumberDifference',
// url: `${URL}/index.html`,
// referenceUrl: 'https://garris.github.io/BackstopJS/?cookie',
// selectors: ['.moneyshot'],
// misMatchThreshold: 0.01,
// requireSameDimensions: true
// },
{
label: 'scenarioSpecificViewports',
url: `${URL}/index.html`,
selectors: ['document', 'viewport'],
viewports: [
{
label: 'Galaxy-S5',
width: 360,
height: 640
}
]
},
{
label: 'scenarioSpecificViewports-withEmptyViewports',
url: `${URL}/index.html`,
viewports: []
},
{
label: 'scenarioSpecificViewports-withMultipleViewports',
url: `${URL}/index.html`,
viewports: [
{
label: 'Pixel-2',
width: 411,
height: 731
},
{
label: 'Pixel2-XL',
width: 411,
height: 823
},
{
label: 'iPhone-X',
width: 375,
height: 812
},
{
label: 'iPad-Pro',
width: 1024,
height: 1366
}
]
},
{
label: 'scenarioSpecificViewports-withExpandSelector',
url: `${URL}/index.html`,
selectors: ['.getItBlock'],
selectorExpansion: true,
viewports: [
{
label: 'iPad-Pro',
width: 1024,
height: 1366
}
]
},
{
label: 'keyPressSelector',
url: `${URL}/examples/featureTests/index.html`,
keyPressSelectors: [
{
selector: 'input[placeholder="Email"]',
keyPress: 'marcdacz@backstopjs.com'
},
{
selector: 'input[placeholder="Password"]',
keyPress: '1234'
}
],
selectors: ['div[id=navbar]'],
postInteractionWait: 1000,
misMatchThreshold: 5,
viewports: [
{
label: 'Desktop',
width: 800,
height: 300
}
]
}
],
paths: {
bitmaps_reference: 'backstop_data/bitmaps_reference',
bitmaps_test: 'backstop_data/bitmaps_test',
engine_scripts: 'backstop_data/engine_scripts',
html_report: 'backstop_data/html_report',
ci_report: 'backstop_data/ci_report'
},
report: ['browser', 'json'],
engine: ENGINE,
engineOptions: {
args: ['--no-sandbox']
},
asyncCaptureLimit: 10,
asyncCompareLimit: 50,
debug: false,
debugWindow: false,
scenarioLogsInReports: true
};
+265
View File
@@ -0,0 +1,265 @@
const ENGINE = 'playwright';
const BROWSER = 'chromium';
const SCRIPT_PATH = 'playwright';
const URL = 'https://garris.github.io/BackstopJS';
module.exports = {
id: `${ENGINE}_${BROWSER}_backstop_features`,
viewports: [
{
label: 'phone',
width: 320,
height: 480
},
{
label: 'tablet',
width: 1024,
height: 768
}
],
onBeforeScript: `${SCRIPT_PATH}/onBefore.js`,
onReadyScript: `${SCRIPT_PATH}/onReady.js`,
scenarios: [
{
label: 'Simple',
url: `${URL}/index.html`
},
{
label: 'pkra bug test',
url: `${URL}/index.html`,
selectors: ['#pkratest', '.logoBlock']
},
{
label: 'delay',
url: `${URL}/index.html?delay`,
delay: 5000,
selectors: ['.getItBlock:nth-child(3)']
},
{
label: 'readyEvent',
url: `${URL}/index.html?delay`,
readyEvent: '_the_lemur_is_ready_to_see_you',
selectors: ['.moneyshot']
},
{
label: 'readyEventTimeout',
url: `${URL}/index.html?delay`,
readyEvent: '_the_lemur_is_ready_to_see_you_timeout',
readyTimeout: 2000,
selectors: ['.moneyshot']
},
{
label: 'readySelector',
url: `${URL}/index.html?delay`,
readySelector: '._the_lemur_is_ready_to_see_you',
selectors: ['.moneyshot']
},
{
label: 'readySelectorTimeout',
url: `${URL}/index.html?delay`,
readySelector: '._the_lemur_is_ready_to_see_you_timeout',
readyTimeout: 2000,
selectors: ['.moneyshot']
},
{
label: 'noDelay',
url: `${URL}/index.html?delay`,
selectors: ['.getItBlock:nth-child(3)']
},
{
label: 'expanded',
url: `${URL}/index.html`,
selectors: ['.getItBlock'],
selectorExpansion: true,
delay: 1000
},
{
label: 'notExpanded',
url: `${URL}/index.html`,
selectors: ['.getItBlock'],
delay: 1000
},
{
label: 'expect',
url: `${URL}/index.html`,
selectors: ['.getItBlock'],
selectorExpansion: true,
expect: 4
},
{
label: 'magicSelectors',
url: `${URL}/index.html`,
selectors: ['document', 'viewport']
},
{
label: 'hideSelectors',
url: `${URL}/index.html`,
hideSelectors: ['.logo-link', 'p']
},
{
label: 'removeSelectors',
url: `${URL}/index.html`,
removeSelectors: ['.logo-link', 'p']
},
{
label: 'notFound',
url: `${URL}/index.html`,
selectors: ['.monkey']
},
{
label: 'notVisible',
url: `${URL}/index.html`,
selectors: ['#noShow']
},
{
label: 'cookies',
cookiePath: 'backstop_data/cookies.json',
url: `${URL}/index.html?cookie`,
selectors: ['.moneyshot']
},
// {
// label: 'customReadyScript',
// onReadyScript: `${SCRIPT_PATH}/overrideCSS.js`,
// url: `${URL}/index.html`,
// selectors: ['.moneyshot']
// },
// {
// label: 'redirect',
// url: `${URL}/index.html`,
// onReadyScript: `${SCRIPT_PATH}/redirect.js`,
// selectors: ['.moneyshot']
// },
{
label: 'hover',
url: `${URL}/index.html?click`,
hoverSelector: '#theLemur',
postInteractionWait: 1000,
selectors: ['.moneyshot']
},
{
label: 'click',
url: `${URL}/index.html?click`,
clickSelector: '#theLemur',
postInteractionWait: '._the_lemur_is_ready_to_see_you',
selectors: ['.moneyshot']
},
{
label: 'scrollToSelector',
url: `${URL}/test/configs/special_cases/scrollToSelector.html`,
scrollToSelector: '.lemurFace',
selectors: ['.lemurFace']
},
// {
// label: 'misMatchThreshold_requireSameDimensions',
// url: `${URL}/index.html`,
// referenceUrl: 'https://garris.github.io/BackstopJS/?cookie',
// selectors: ['.moneyshot'],
// misMatchThreshold: 6.0,
// requireSameDimensions: false
// },
// {
// label: 'misMatchThreshold_realNumberDifference',
// url: `${URL}/index.html`,
// referenceUrl: 'https://garris.github.io/BackstopJS/?cookie',
// selectors: ['.moneyshot'],
// misMatchThreshold: 0.01,
// requireSameDimensions: true
// },
{
label: 'scenarioSpecificViewports',
url: `${URL}/index.html`,
selectors: ['document', 'viewport'],
viewports: [
{
label: 'Galaxy-S5',
width: 360,
height: 640
}
]
},
{
label: 'scenarioSpecificViewports-withEmptyViewports',
url: `${URL}/index.html`,
viewports: []
},
{
label: 'scenarioSpecificViewports-withMultipleViewports',
url: `${URL}/index.html`,
viewports: [
{
label: 'Pixel-2',
width: 411,
height: 731
},
{
label: 'Pixel2-XL',
width: 411,
height: 823
},
{
label: 'iPhone-X',
width: 375,
height: 812
},
{
label: 'iPad-Pro',
width: 1024,
height: 1366
}
]
},
{
label: 'scenarioSpecificViewports-withExpandSelector',
url: `${URL}/index.html`,
selectors: ['.getItBlock'],
selectorExpansion: true,
viewports: [
{
label: 'iPad-Pro',
width: 1024,
height: 1366
}
]
},
{
label: 'keyPressSelector',
url: `${URL}/examples/featureTests/index.html`,
keyPressSelectors: [
{
selector: 'input[placeholder="Email"]',
keyPress: 'marcdacz@backstopjs.com'
},
{
selector: 'input[placeholder="Password"]',
keyPress: '1234'
}
],
selectors: ['div[id=navbar]'],
postInteractionWait: 1000,
misMatchThreshold: 5,
viewports: [
{
label: 'Desktop',
width: 800,
height: 300
}
]
}
],
paths: {
bitmaps_reference: 'backstop_data/bitmaps_reference',
bitmaps_test: 'backstop_data/bitmaps_test',
engine_scripts: 'backstop_data/engine_scripts',
html_report: 'backstop_data/html_report',
ci_report: 'backstop_data/ci_report'
},
report: ['browser', 'json'],
engine: `${ENGINE}`,
engineOptions: {
browser: `${BROWSER}`
},
asyncCaptureLimit: 10,
asyncCompareLimit: 50,
debug: false,
debugWindow: false
};
+110
View File
@@ -0,0 +1,110 @@
#!/usr/bin/env node
// EXAMPLE COMMAND
// ~/Development/BackstopJS/test/configs
// $ node dynamic_node_app --dynamicTestId=6 --testLabel="dynamic test" --scenarioLabel="one" --url=https://garris.github.io/BackstopJS?cookie --command=test
// THIS IS A DEMO OF DYNAMIC SCENARIO MODE
//
// - Each time the above command is run with a unique scenarioLabel and URL a new scenario is created and added to a test identified by dynamicTestId.
//
// - In this way you can create an arbitrary test creating new scenarios on-the-fly.
//
// - Once you have created initial test data, commands like `--command=approve` will operate as expected using scenario labels defined on the most recent run.
//
// - Subsequent `test` runs using a unique dynamicTestId value will create new dynamic scenarios where scenarioLabel and URL values that were approved
// on previous runs will compared against the current unique test run.
//
// - This mode is intended for integrating with external test runners such as qunit -- like in the https://github.com/garris/ember-backstop project.
//
const assert = require('assert').strict;
const parseArgs = require('minimist');
const backstop = require('../../core/runner');
const URL = 'https://garris.github.io/BackstopJS';
const argsOptions = parseArgs(process.argv.slice(2), {
string: ['dynamicTestId', 'testLabel', 'scenarioLabel', 'url', 'command'],
default: {
url: URL
}
});
console.log('Dynamic test example.');
console.log(`config: ${JSON.stringify(argsOptions, null, 2)}`);
assert.ok(argsOptions.dynamicTestId, 'Hold on there: dynamicTestId must represent a unique identifier (string or int) for each test run.');
/**
* A config used to test explicitly setting a config.
* @type {Object}
*/
const exampleConfig = {
i: true, // incremental flag -- suppresses cleaning reference directory during reference command
config: {
dynamicTestId: argsOptions.dynamicTestId, // when truthy backstop will assume one dynamic scenario which is appended to test report belonging to dynamicTestId
id: argsOptions.testLabel,
viewports: [
{
label: 'phone',
width: 320,
height: 480
},
{
label: 'tablet',
width: 1024,
height: 768
}
],
scenarios: [
{
label: argsOptions.scenarioLabel,
url: argsOptions.url
}
],
paths: {
bitmaps_reference: 'backstop_data/bitmaps_reference',
bitmaps_test: 'backstop_data/bitmaps_test',
engine_scripts: 'backstop_data/engine_scripts',
html_report: 'backstop_data/html_report',
json_report: 'backstop_data/json_report'
},
report: ['browser', 'json'],
engine: 'puppeteer',
asyncCaptureLimit: 5,
asyncCompareLimit: 50,
debug: false,
debugWindow: false
}
};
function approve () {
backstop('approve', exampleConfig);
}
function open () {
backstop('openReport', exampleConfig);
}
function main () {
backstop('test', exampleConfig).then(
() => {
console.log('No changes found.');
},
() => {
console.log('Changes found.');
}
);
}
switch (argsOptions.command) {
case 'approve':
approve();
break;
case 'open':
case 'openReport':
open();
break;
default:
main();
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Some files were not shown because too many files have changed in this diff Show More