2019-05-20 15:22:50 +08:00
|
|
|
/*eslint no-console: "off"*/
|
|
|
|
|
2017-12-18 16:32:58 +08:00
|
|
|
const args = process.argv.slice(2);
|
|
|
|
|
|
|
|
if (args.length < 1 || args.length > 2) {
|
|
|
|
console.log("Expecting: node {smoke_test.js} {url}");
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
const url = args[0];
|
|
|
|
|
|
|
|
console.log(`Starting Discourse Smoke Test for ${url}`);
|
|
|
|
|
2021-12-13 17:31:49 +08:00
|
|
|
const chromeLauncher = require("chrome-launcher");
|
|
|
|
const puppeteer = require("puppeteer-core");
|
2018-09-11 09:16:03 +08:00
|
|
|
const path = require("path");
|
2017-12-18 16:32:58 +08:00
|
|
|
|
|
|
|
(async () => {
|
|
|
|
const browser = await puppeteer.launch({
|
2021-12-13 17:31:49 +08:00
|
|
|
executablePath: chromeLauncher.Launcher.getInstallations()[0],
|
2021-05-21 09:43:47 +08:00
|
|
|
// when debugging locally setting the SHOW_BROWSER env variable can be very helpful
|
2019-10-24 23:51:47 +08:00
|
|
|
headless: process.env.SHOW_BROWSER === undefined,
|
2021-12-13 17:31:49 +08:00
|
|
|
args: ["--disable-local-storage", "--no-sandbox"],
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
const page = await browser.newPage();
|
|
|
|
|
2018-05-28 10:58:36 +08:00
|
|
|
await page.setViewport({
|
2017-12-18 16:32:58 +08:00
|
|
|
width: 1366,
|
|
|
|
height: 768
|
2018-05-28 10:58:36 +08:00
|
|
|
});
|
2017-12-18 16:32:58 +08:00
|
|
|
|
2018-05-28 14:19:12 +08:00
|
|
|
const takeFailureScreenshot = function() {
|
2018-09-11 09:16:03 +08:00
|
|
|
const screenshotPath = `${process.env.SMOKE_TEST_SCREENSHOT_PATH ||
|
2019-06-13 09:14:17 +08:00
|
|
|
"tmp/smoke-test-screenshots"}/smoke-test-${Date.now()}.png`;
|
2018-05-28 14:19:12 +08:00
|
|
|
console.log(`Screenshot of failure taken at ${screenshotPath}`);
|
|
|
|
return page.screenshot({ path: screenshotPath, fullPage: true });
|
|
|
|
};
|
|
|
|
|
2017-12-18 16:32:58 +08:00
|
|
|
const exec = (description, fn, assertion) => {
|
|
|
|
const start = +new Date();
|
|
|
|
|
2018-09-11 09:16:03 +08:00
|
|
|
return fn
|
|
|
|
.call()
|
|
|
|
.then(async output => {
|
|
|
|
if (assertion) {
|
|
|
|
if (assertion.call(this, output)) {
|
|
|
|
console.log(`PASSED: ${description} - ${+new Date() - start}ms`);
|
|
|
|
} else {
|
|
|
|
console.log(`FAILED: ${description} - ${+new Date() - start}ms`);
|
|
|
|
await takeFailureScreenshot();
|
|
|
|
console.log("SMOKE TEST FAILED");
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2017-12-18 16:32:58 +08:00
|
|
|
} else {
|
2018-09-11 09:16:03 +08:00
|
|
|
console.log(`PASSED: ${description} - ${+new Date() - start}ms`);
|
2017-12-18 16:32:58 +08:00
|
|
|
}
|
2018-09-11 09:16:03 +08:00
|
|
|
})
|
|
|
|
.catch(async error => {
|
|
|
|
console.log(
|
|
|
|
`ERROR (${description}): ${error.message} - ${+new Date() - start}ms`
|
|
|
|
);
|
|
|
|
await takeFailureScreenshot();
|
|
|
|
console.log("SMOKE TEST FAILED");
|
|
|
|
process.exit(1);
|
|
|
|
});
|
2017-12-18 16:32:58 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
const assert = (description, fn, assertion) => {
|
|
|
|
return exec(description, fn, assertion);
|
|
|
|
};
|
|
|
|
|
2018-09-11 09:16:03 +08:00
|
|
|
page.on("console", msg => console.log(`PAGE LOG: ${msg.text()}`));
|
2017-12-18 16:32:58 +08:00
|
|
|
|
2018-09-11 09:16:03 +08:00
|
|
|
page.on("response", resp => {
|
2020-05-15 03:04:09 +08:00
|
|
|
if (resp.status() !== 200 && resp.status() !== 302) {
|
2018-09-11 09:16:03 +08:00
|
|
|
console.log(
|
|
|
|
"FAILED HTTP REQUEST TO " + resp.url() + " Status is: " + resp.status()
|
|
|
|
);
|
2022-01-07 16:50:59 +08:00
|
|
|
if (resp.status() === 429) {
|
|
|
|
const headers = resp.headers();
|
|
|
|
console.log("Response headers:");
|
|
|
|
Object.keys(headers).forEach((key) => {
|
|
|
|
console.log(`${key}: ${headers[key]}`);
|
|
|
|
});
|
|
|
|
}
|
2018-05-17 14:00:43 +08:00
|
|
|
}
|
|
|
|
return resp;
|
|
|
|
});
|
|
|
|
|
2018-01-03 14:24:06 +08:00
|
|
|
if (process.env.AUTH_USER && process.env.AUTH_PASSWORD) {
|
|
|
|
await exec("basic authentication", () => {
|
2019-10-24 07:42:26 +08:00
|
|
|
return page.authenticate({
|
|
|
|
username: process.env.AUTH_USER,
|
|
|
|
password: process.env.AUTH_PASSWORD
|
2018-01-03 14:24:06 +08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-11-15 01:24:43 +08:00
|
|
|
const login = async function() {
|
|
|
|
await exec("open login modal", () => {
|
|
|
|
return page.click(".login-button");
|
|
|
|
});
|
|
|
|
|
|
|
|
await exec("login modal is open", () => {
|
|
|
|
return page.waitForSelector(".login-modal", { visible: true });
|
|
|
|
});
|
|
|
|
|
|
|
|
await exec("type in credentials & log in", () => {
|
|
|
|
let promise = page.type(
|
|
|
|
"#login-account-name",
|
|
|
|
process.env.DISCOURSE_USERNAME || "smoke_user"
|
|
|
|
);
|
|
|
|
|
|
|
|
promise = promise.then(() => {
|
|
|
|
return page.type(
|
|
|
|
"#login-account-password",
|
|
|
|
process.env.DISCOURSE_PASSWORD || "P4ssw0rd"
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
promise = promise.then(() => {
|
|
|
|
return page.click(".login-modal .btn-primary");
|
|
|
|
});
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
});
|
|
|
|
|
|
|
|
await exec("is logged in", () => {
|
|
|
|
return page.waitForSelector(".current-user", { visible: true });
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2017-12-21 10:32:14 +08:00
|
|
|
await exec("go to site", () => {
|
|
|
|
return page.goto(url);
|
|
|
|
});
|
2017-12-18 16:32:58 +08:00
|
|
|
|
|
|
|
await exec("expect a log in button in the header", () => {
|
2020-01-07 20:58:08 +08:00
|
|
|
return page.waitForSelector("header .login-button", { visible: true });
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
2019-11-15 01:24:43 +08:00
|
|
|
if (process.env.LOGIN_AT_BEGINNING) {
|
|
|
|
await login();
|
|
|
|
}
|
|
|
|
|
2017-12-18 16:32:58 +08:00
|
|
|
await exec("go to latest page", () => {
|
2018-09-11 09:16:03 +08:00
|
|
|
return page.goto(path.join(url, "latest"));
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("at least one topic shows up", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.waitForSelector(".topic-list tbody tr", { visible: true });
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("go to categories page", () => {
|
2018-09-11 09:16:03 +08:00
|
|
|
return page.goto(path.join(url, "categories"));
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("can see categories on the page", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.waitForSelector(".category-list", { visible: true });
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("navigate to 1st topic", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.click(".main-link a.title:first-of-type");
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("at least one post body", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.waitForSelector(".topic-post", { visible: true });
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("click on the 1st user", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.click(".topic-meta-data a:first-of-type");
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("user has details", () => {
|
2020-02-13 17:58:17 +08:00
|
|
|
return page.waitForSelector(".user-card .names", { visible: true });
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
if (!process.env.READONLY_TESTS) {
|
2019-11-15 01:24:43 +08:00
|
|
|
if (!process.env.LOGIN_AT_BEGINNING) {
|
|
|
|
await login();
|
|
|
|
}
|
2017-12-18 16:32:58 +08:00
|
|
|
|
2019-11-15 01:24:43 +08:00
|
|
|
await exec("go home", () => {
|
2020-01-07 20:54:01 +08:00
|
|
|
let promise = page.waitForSelector("#site-logo, #site-text-logo", {
|
|
|
|
visible: true
|
|
|
|
});
|
2017-12-18 16:32:58 +08:00
|
|
|
|
|
|
|
promise = promise.then(() => {
|
2019-11-15 01:24:43 +08:00
|
|
|
return page.click("#site-logo, #site-text-logo");
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
});
|
|
|
|
|
|
|
|
await exec("it shows a topic list", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.waitForSelector(".topic-list", { visible: true });
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("we have a create topic button", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.waitForSelector("#create-topic", { visible: true });
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("open composer", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.click("#create-topic");
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("the editor is visible", () => {
|
|
|
|
return page.waitForFunction(
|
2017-12-20 21:41:49 +08:00
|
|
|
"document.activeElement === document.getElementById('reply-title')"
|
2017-12-18 16:32:58 +08:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2019-05-09 14:02:45 +08:00
|
|
|
await page.evaluate(() => {
|
|
|
|
document.getElementById("reply-title").value = "";
|
|
|
|
});
|
|
|
|
|
2017-12-18 16:32:58 +08:00
|
|
|
await exec("compose new topic", () => {
|
2018-09-11 09:16:03 +08:00
|
|
|
const date = `(${+new Date()})`;
|
2017-12-18 16:32:58 +08:00
|
|
|
const title = `This is a new topic ${date}`;
|
|
|
|
const post = `I can write a new topic inside the smoke test! ${date} \n\n`;
|
|
|
|
|
|
|
|
let promise = page.type("#reply-title", title);
|
|
|
|
|
|
|
|
promise = promise.then(() => {
|
|
|
|
return page.type("#reply-control .d-editor-input", post);
|
|
|
|
});
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
});
|
|
|
|
|
|
|
|
await exec("updates preview", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.waitForSelector(".d-editor-preview p", { visible: true });
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("submit the topic", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.click(".submit-panel .create");
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("topic is created", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.waitForSelector(".fancy-title", { visible: true });
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("open the composer", () => {
|
2017-12-20 21:41:49 +08:00
|
|
|
return page.click(".post-controls:first-of-type .create");
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("composer is open", () => {
|
2018-09-11 09:16:03 +08:00
|
|
|
return page.waitForSelector("#reply-control .d-editor-input", {
|
|
|
|
visible: true
|
|
|
|
});
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
await exec("compose reply", () => {
|
2018-09-11 09:16:03 +08:00
|
|
|
const post = `I can even write a reply inside the smoke test ;) (${+new Date()})`;
|
2017-12-18 16:32:58 +08:00
|
|
|
return page.type("#reply-control .d-editor-input", post);
|
|
|
|
});
|
|
|
|
|
2018-06-05 16:58:15 +08:00
|
|
|
await exec("waiting for the preview", () => {
|
2018-09-11 09:16:03 +08:00
|
|
|
return page.waitForXPath(
|
|
|
|
"//div[contains(@class, 'd-editor-preview') and contains(.//p, 'I can even write a reply')]",
|
2017-12-20 21:41:49 +08:00
|
|
|
{ visible: true }
|
2017-12-18 16:32:58 +08:00
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2019-10-25 00:36:27 +08:00
|
|
|
await exec("wait a little bit", () => {
|
|
|
|
return page.waitFor(5000);
|
|
|
|
});
|
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
await exec("submit the reply", () => {
|
|
|
|
let promise = page.click("#reply-control .create");
|
2019-05-22 01:01:29 +08:00
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
promise = promise.then(() => {
|
|
|
|
return page.waitForSelector("#reply-control.closed", {
|
|
|
|
visible: false
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return promise;
|
2017-12-18 16:32:58 +08:00
|
|
|
});
|
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
await assert("reply is created", () => {
|
|
|
|
let promise = page.waitForSelector(
|
|
|
|
".topic-post:not(.staged) #post_2 .cooked",
|
|
|
|
{
|
|
|
|
visible: true
|
|
|
|
}
|
|
|
|
);
|
2017-12-18 16:32:58 +08:00
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
promise = promise.then(() => {
|
|
|
|
return page.waitForFunction(
|
|
|
|
"document.querySelector('#post_2 .cooked').innerText.includes('I can even write a reply')"
|
|
|
|
);
|
|
|
|
});
|
2017-12-18 16:32:58 +08:00
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
return promise;
|
|
|
|
});
|
2019-05-21 17:44:30 +08:00
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
await exec("wait a little bit", () => {
|
|
|
|
return page.waitFor(5000);
|
|
|
|
});
|
2019-05-21 17:44:30 +08:00
|
|
|
|
|
|
|
await exec("open composer to edit first post", () => {
|
2019-10-24 23:51:47 +08:00
|
|
|
let promise = page.evaluate(() => {
|
|
|
|
window.scrollTo(0, 0);
|
|
|
|
});
|
2019-05-21 17:44:30 +08:00
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
promise = promise.then(() => {
|
|
|
|
return page.click("#post_1 .post-controls .edit");
|
2019-05-21 17:44:30 +08:00
|
|
|
});
|
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
promise = promise.then(() => {
|
|
|
|
return page.waitForSelector("#reply-control .d-editor-input", {
|
|
|
|
visible: true
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
});
|
|
|
|
|
|
|
|
await exec("update post raw in composer", () => {
|
|
|
|
let promise = page.waitFor(5000);
|
2019-05-21 17:44:30 +08:00
|
|
|
|
|
|
|
promise = promise.then(() => {
|
2019-10-24 23:51:47 +08:00
|
|
|
return page.type(
|
|
|
|
"#reply-control .d-editor-input",
|
|
|
|
"\n\nI edited this post"
|
|
|
|
);
|
2019-05-21 17:44:30 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
});
|
|
|
|
|
|
|
|
await exec("submit the edit", () => {
|
2019-10-24 23:51:47 +08:00
|
|
|
let promise = page.click("#reply-control .create");
|
2019-05-21 17:44:30 +08:00
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
promise = promise.then(() => {
|
|
|
|
return page.waitForSelector("#reply-control.closed", {
|
2019-05-21 17:44:30 +08:00
|
|
|
visible: false
|
|
|
|
});
|
2019-10-24 23:51:47 +08:00
|
|
|
});
|
2019-05-21 17:44:30 +08:00
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
return promise;
|
|
|
|
});
|
2019-05-21 17:44:30 +08:00
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
await assert("edit is successful", () => {
|
|
|
|
let promise = page.waitForSelector(
|
|
|
|
".topic-post:not(.staged) #post_1 .cooked",
|
|
|
|
{
|
|
|
|
visible: true
|
|
|
|
}
|
|
|
|
);
|
2019-05-21 17:44:30 +08:00
|
|
|
|
2019-10-24 23:51:47 +08:00
|
|
|
promise = promise.then(() => {
|
|
|
|
return page.waitForFunction(
|
|
|
|
"document.querySelector('#post_1 .cooked').innerText.includes('I edited this post')"
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
return promise;
|
|
|
|
});
|
2017-12-18 16:32:58 +08:00
|
|
|
}
|
|
|
|
|
2017-12-23 09:29:22 +08:00
|
|
|
await exec("close browser", () => {
|
|
|
|
return browser.close();
|
|
|
|
});
|
2017-12-18 16:32:58 +08:00
|
|
|
|
|
|
|
console.log("ALL PASSED");
|
|
|
|
})();
|