web: Test harness
We have an end-to-end test harness that includes a trivially correct DSL for "This is what a user would do, do this": ``` const deleteProvider = (theSlug) => ([ ["button", '>>>ak-sidebar-item a[href="#/core/providers"]'], ["deletebox", `>>>a[href="#/core/applications/${theSlug}"]`], ["button", '>>>ak-forms-delete-bulk button[slot="trigger"]'], ["button", '>>>ak-forms-delete-bulk div[role="dialog"] ak-spinner-button'], ]); ``` It's now possible to target individual sequences of events this way. With a little creativity, we could have standalone functions that take parameters for our calls and just do them, without too much struggle.
This commit is contained in:
parent
e1351bd999
commit
c8512c3116
|
@ -1,7 +1,3 @@
|
|||
# '.PHONY' entries inform Make that there is no accompanying file in the
|
||||
# repository that this command actually makes, and so Make should always run the
|
||||
# command, rather than look for dependencies to see if it should be run.
|
||||
|
||||
.PHONY: help
|
||||
help: ## Print out this help message.
|
||||
@M=$$(perl -ne 'm/((\w|-)*):.*##/ && print length($$1)."\n"' Makefile | \
|
||||
|
@ -16,80 +12,13 @@ update-local-chromedriver: ## Update the chrome driver to match the local chrom
|
|||
@ scripts/update_local_chromedriver
|
||||
|
||||
.PHONY: check-chromedriver
|
||||
check-chromedriver:
|
||||
check-chromedriver: ## Report if the chrome driver and the local chrome version match
|
||||
@ scripts/check_local_chromedriver
|
||||
|
||||
RUNNER=npx wdio wdio.conf.js --spec ./tests/application_runner.js
|
||||
RUNNER=npx wdio wdio.conf.js
|
||||
|
||||
.PHONY: test-home-complex
|
||||
test-home-complex: check-chromedriver ## Run the "Complex Home Application" test
|
||||
${RUNNER} --application=./portfolios/home_full_home_application.json
|
||||
.PHONY: application-plus-ldap
|
||||
application-plus-ldap: check-chromedriver ## Run the "Wizard: Application With LDAP Provider, successful" test
|
||||
@ ${RUNNER} --spec=./tests/application-plus-ldap.test.js
|
||||
|
||||
.PHONY: test-home-carriage-hill
|
||||
test-home-carriage-hill: check-chromedriver ## Run the "Carriage Hill" test
|
||||
${RUNNER} --application=./portfolios/home_carriage-hill.json
|
||||
|
||||
.PHONY: test-home-mobile-home
|
||||
test-home-mobile-home: check-chromedriver ## Run the "Mobile Home" test
|
||||
${RUNNER} --application=./portfolios/home_mobile_home_application.json
|
||||
|
||||
.PHONY: test-home-florida-roofs
|
||||
test-home-florida-roofs: check-chromedriver ## Run the "Floride Roofs" test
|
||||
${RUNNER} --application=./portfolios/home_florida_roofs_gates_and_farms.json
|
||||
|
||||
.PHONY: test-home-townhome
|
||||
test-home-townhome: check-chromedriver ## Run the "Townhome / Policy Lapsed" test
|
||||
${RUNNER} --application=./portfolios/home_townhome_application.json
|
||||
|
||||
.PHONY: test-home-future-application
|
||||
test-home-future-application: check-chromedriver ## Run the "Future Purchase" test
|
||||
${RUNNER} --application=./portfolios/home_future_purchase_application.json
|
||||
|
||||
.PHONY: test-home-basic
|
||||
test-home-basic: check-chromedriver ## Run the "Basic Home Application" test
|
||||
${RUNNER} --application=./portfolios/home_application_with_error.json
|
||||
|
||||
.PHONY: test-home-and-auto
|
||||
test-home-and-auto: check-chromedriver ## Run the "Home & Auto" test
|
||||
${RUNNER} --application=./portfolios/ha_application.json
|
||||
|
||||
.PHONY: test-auto-full
|
||||
test-auto-full: check-chromedriver ## Run the "Auto, Two Drivers" test
|
||||
${RUNNER} --application=./portfolios/auto_two_driver_auto_application.json
|
||||
|
||||
.PHONY: test-auto-basic
|
||||
test-auto-basic: check-chromedriver ## Run the "Basic Auto" test
|
||||
${RUNNER} --application=./portfolios/auto_application.json
|
||||
|
||||
.PHONY: test-auto-two-cars
|
||||
test-auto-two-cars: check-chromedriver ## Run the "Two-Cars Auto" test
|
||||
${RUNNER} --application=./portfolios/auto_application_two_cars.json
|
||||
|
||||
.PHONY: test-auto-michigan
|
||||
test-auto-michigan: check-chromedriver ## Run the "Michigan Auto" test
|
||||
${RUNNER} --application=./portfolios/auto_michigan_auto_application.json
|
||||
|
||||
.PHONY: test-smoke
|
||||
test-smoke: check-chromedriver ## Run the "Complex Home, Home+Auto and Two-Cars Auto" tests, including unanswered flows
|
||||
${RUNNER} --application=./portfolios/ha_application.json \
|
||||
--application=./portfolios/home_full_home_application.json \
|
||||
--application=./portfolios/unanswered_ha_application.json \
|
||||
--application=./portfolios/auto_application_two_cars.json \
|
||||
--application=./portfolios/windmit/home_florida_windmit_empty.json \
|
||||
--application=./portfolios/windmit/home_florida_windmit_full.json
|
||||
|
||||
.PHONY: test-embedded
|
||||
test-embedded: check-chromedriver ## Run the "Complex Home, Home+Auto and Two-Cars Auto" tests, including unanswered flows in embedded mode
|
||||
BOWTIE_EMBEDDED=true ${RUNNER} --application=./portfolios/ha_application.json \
|
||||
--application=./portfolios/home_future_purchase_application.json \
|
||||
--application=./portfolios/home_full_home_application.json \
|
||||
--application=./portfolios/unanswered_ha_application.json \
|
||||
--application=./portfolios/auto_application_two_cars.json
|
||||
|
||||
.PHONY: test-all
|
||||
test-all: check-chromedriver ## Run all the tests! Warning: this currently takes about 20 minutes!
|
||||
${RUNNER}
|
||||
|
||||
.PHONY: scan-for-duplicate-ids
|
||||
scan-for-duplicate-ids: check-chromedriver ## Run 'Home Basic', cataloging duplicates. Warning: SLOW
|
||||
npx wdio wdio.conf.js --spec ./tests/application_runner_with_dupe_scanner.js --application=./portfolios/home_application.json
|
||||
|
|
|
@ -8,7 +8,6 @@ async function text(selector, value) {
|
|||
}
|
||||
|
||||
async function button(selector) {
|
||||
console.log("HEY:", selector);
|
||||
const button = await $(selector);
|
||||
return await button.click();
|
||||
}
|
||||
|
@ -28,9 +27,25 @@ async function pause(selector) {
|
|||
return await browser.pause(CLICK_TIME_DELAY);
|
||||
}
|
||||
|
||||
async function waitfor(selector) {
|
||||
return await $(selector).waitForDisplayed();
|
||||
}
|
||||
|
||||
async function deletebox(selector) {
|
||||
return await $(selector)
|
||||
.parentElement()
|
||||
.parentElement()
|
||||
.$(".pf-c-table__check")
|
||||
.$('input[type="checkbox"]')
|
||||
.click();
|
||||
}
|
||||
|
||||
|
||||
exports.$AkSel = {
|
||||
button,
|
||||
pause,
|
||||
search,
|
||||
text,
|
||||
waitfor,
|
||||
deletebox,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
function randomPrefix() {
|
||||
let dt = new Date().getTime();
|
||||
return "xxxxxxxx".replace(/x/g, (c) => {
|
||||
const r = (dt + Math.random() * 16) % 16 | 0;
|
||||
dt = Math.floor(dt / 16);
|
||||
return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
function convertToSlug(text) {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(/ /g, "-")
|
||||
.replace(/[^\w-]+/g, "");
|
||||
}
|
||||
|
||||
module.exports = { randomPrefix, convertToSlug };
|
|
@ -0,0 +1,93 @@
|
|||
const { execSync } = require("child_process");
|
||||
const { readdirSync } = require("fs");
|
||||
const path = require("path");
|
||||
const { $AkSel } = require("../lib/idiom");
|
||||
const { randomPrefix, convertToSlug } = require("../lib/utils");
|
||||
|
||||
const CLICK_TIME_DELAY = 250;
|
||||
|
||||
const login = [
|
||||
["text", '>>>input[name="uidField"]', "ken@goauthentik.io"],
|
||||
["button", '>>>button[type="submit"]'],
|
||||
["pause"],
|
||||
["text", '>>>input[name="password"]', "eat10bugs"],
|
||||
["button", '>>>button[type="submit"]'],
|
||||
["pause", ">>>div.header h1"],
|
||||
];
|
||||
|
||||
const navigateToWizard = [
|
||||
["button", '>>>a[href="/if/admin"]'],
|
||||
["waitfor", ">>>ak-admin-overview"],
|
||||
["button", '>>>a[href="#/core/applications;%7B%22createForm%22%3Atrue%7D"]'],
|
||||
["waitfor", ">>>ak-application-list"],
|
||||
["button", '>>>ak-wizard-frame button[slot="trigger"]']
|
||||
];
|
||||
|
||||
// prettier-ignore
|
||||
const ldapApplication = (theCode) => ([
|
||||
["text", '>>>ak-form-element-horizontal input[name="name"]', `This Is My Application ${theCode}`],
|
||||
["button", ">>>ak-wizard-frame footer button.pf-m-primary"],
|
||||
["button", '>>>input[value="ldapprovider"]'],
|
||||
["button", ">>>ak-wizard-frame footer button.pf-m-primary"],
|
||||
["search", '>>>ak-tenanted-flow-search input[type="text"]', "button*=default-authentication-flow",],
|
||||
["text", '>>>ak-form-element-horizontal input[name="tlsServerName"]', "example.goauthentik.io"],
|
||||
["button", ">>>ak-wizard-frame footer button.pf-m-primary"],
|
||||
]);
|
||||
|
||||
const deleteProvider = (theSlug) => ([
|
||||
["button", '>>>ak-sidebar-item a[href="#/core/providers"]'],
|
||||
["deletebox", `>>>a[href="#/core/applications/${theSlug}"]`],
|
||||
["button", '>>>ak-forms-delete-bulk button[slot="trigger"]'],
|
||||
["button", '>>>ak-forms-delete-bulk div[role="dialog"] ak-spinner-button'],
|
||||
]);
|
||||
|
||||
const deleteApplication = (theSlug) => ([
|
||||
["button", '>>>ak-sidebar-item a[href="#/core/applications"'],
|
||||
["deletebox", `>>>a[href="#/core/applications/${theSlug}"]`],
|
||||
["button", '>>>ak-forms-delete-bulk button[slot="trigger"]'],
|
||||
["button", '>>>ak-forms-delete-bulk div[role="dialog"] ak-spinner-button']
|
||||
]);
|
||||
|
||||
|
||||
function prepApplicationAndSlug() {
|
||||
const newSuffix = randomPrefix();
|
||||
const thisApplication = ldapApplication(newSuffix);
|
||||
return [thisApplication, convertToSlug(thisApplication[0][2])];
|
||||
}
|
||||
|
||||
async function runSequence(sequence, watch = false) {
|
||||
for ([command, ...args] of sequence) {
|
||||
await $AkSel[command].apply($, args);
|
||||
if (watch) {
|
||||
await browser.pause(250);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe("Login", () => {
|
||||
it("Should correctly log in to Authentik}", async () => {
|
||||
const [theApplication, theSlug] = prepApplicationAndSlug();
|
||||
|
||||
await browser.reloadSession();
|
||||
await browser.url("http://localhost:9000");
|
||||
|
||||
runSequence(login, true);
|
||||
|
||||
const home = await $(">>>div.header h1");
|
||||
await expect(home).toHaveText("My applications");
|
||||
|
||||
await runSequence(navigateToWizard, true);
|
||||
await runSequence(theApplication, true)
|
||||
|
||||
const success = await $(">>>ak-application-wizard-commit-application h1.pf-c-title");
|
||||
await expect(success).toHaveText("Your application has been saved");
|
||||
|
||||
await $AkSel.button(">>>ak-wizard-frame .pf-c-wizard__footer-cancel button");
|
||||
|
||||
await runSequence(deleteProvider(theSlug), true);
|
||||
await runSequence(deleteApplication(theSlug), true);
|
||||
|
||||
const expectedApplication = await $(`>>>a[href="#/core/applications/${theSlug}"]`);
|
||||
expect(expectedApplication).not.toExist();
|
||||
});
|
||||
});
|
|
@ -1,61 +0,0 @@
|
|||
const { execSync } = require("child_process");
|
||||
const { readdirSync } = require("fs");
|
||||
const path = require("path");
|
||||
const { $AkSel } = require("../lib/idiom");
|
||||
|
||||
const CLICK_TIME_DELAY = 250;
|
||||
|
||||
const login = [
|
||||
["text", '>>>input[name="uidField"]', "ken@goauthentik.io"],
|
||||
["button", '>>>button[type="submit"]'],
|
||||
["pause"],
|
||||
["text", '>>>input[name="password"]', "eat10bugs"],
|
||||
["button", '>>>button[type="submit"]'],
|
||||
["pause", ">>>div.header h1"],
|
||||
];
|
||||
|
||||
const simpleApplication = [
|
||||
["text", '>>>ak-form-element-horizontal input[name="name"]', "This Is My Application"],
|
||||
["button", ">>>ak-wizard-frame footer button.pf-m-primary"],
|
||||
["button", '>>>input[value="ldapprovider"]'],
|
||||
["button", ">>>ak-wizard-frame footer button.pf-m-primary"],
|
||||
["text", '>>>ak-form-element-horizontal input[name="name"]', "This Is My Provider"],
|
||||
[
|
||||
"search",
|
||||
'>>>ak-tenanted-flow-search input[type="text"]',
|
||||
"button*=default-authentication-flow",
|
||||
],
|
||||
["text", '>>>ak-form-element-horizontal input[name="tlsServerName"]', "example.goauthentik.io"],
|
||||
["button", ">>>ak-wizard-frame footer button.pf-m-primary"],
|
||||
];
|
||||
|
||||
describe("Login", () => {
|
||||
it("Should correctly log in to Authentik}", async () => {
|
||||
await browser.reloadSession();
|
||||
await browser.url("http://localhost:9000");
|
||||
|
||||
let start = Date.now();
|
||||
for ([command, ...args] of login) {
|
||||
await $AkSel[command].apply($, args);
|
||||
}
|
||||
|
||||
const home = await $(">>>div.header h1");
|
||||
expect(home).toHaveText("My applications");
|
||||
|
||||
const goToAdmin = await $('>>>a[href="/if/admin"]');
|
||||
goToAdmin.click();
|
||||
|
||||
await $(">>>ak-admin-overview").waitForDisplayed();
|
||||
$AkSel.button('>>>a[href="#/core/applications;%7B%22createForm%22%3Atrue%7D"]');
|
||||
|
||||
await $(">>>ak-application-list").waitForDisplayed();
|
||||
$AkSel.button('>>>ak-wizard-frame button[slot="trigger"]');
|
||||
|
||||
for ([command, ...args] of simpleApplication) {
|
||||
await $AkSel[command].apply($, args);
|
||||
}
|
||||
|
||||
let timeTaken = Date.now() - start;
|
||||
console.log("Total time taken : " + timeTaken + " milliseconds");
|
||||
});
|
||||
});
|
|
@ -33,7 +33,8 @@ export class ApplicationWizardApplicationDetails extends BasePanel {
|
|||
}
|
||||
|
||||
validator() {
|
||||
return this.form.reportValidity();
|
||||
return true;
|
||||
// return this.form.reportValidity();
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
|
|
Reference in New Issue