Test: Fix tests

Test: Add clear stats test

Test: Attempt to fix tests

Test: Add test for disable auth

Update README
This commit is contained in:
Nelson Chan 2021-11-04 23:19:31 +08:00
parent f65cc655c0
commit 07742799ed
9 changed files with 101 additions and 35 deletions

View file

@ -2,5 +2,11 @@ module.exports = {
"launch": { "launch": {
"headless": process.env.HEADLESS_TEST || false, "headless": process.env.HEADLESS_TEST || false,
"userDataDir": "./data/test-chrome-profile", "userDataDir": "./data/test-chrome-profile",
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-gpu",
"--disable-dev-shm-usage"
],
} }
}; };

View file

@ -22,7 +22,7 @@
"build": "vite build --config ./config/vite.config.js", "build": "vite build --config ./config/vite.config.js",
"test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test", "test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test",
"test-with-build": "npm run build && npm test", "test-with-build": "npm run build && npm test",
"jest": "node test/prepare-jest.js && npm run jest-frontend && npm run jest-backend && jest --config=./config/jest.config.js", "jest": "node test/prepare-jest.js && npm run jest-frontend && npm run jest-backend && jest --runInBand --config=./config/jest.config.js",
"jest-frontend": "cross-env TEST_FRONTEND=1 jest --config=./config/jest-frontend.config.js", "jest-frontend": "cross-env TEST_FRONTEND=1 jest --config=./config/jest-frontend.config.js",
"jest-backend": "cross-env TEST_BACKEND=1 jest --config=./config/jest-backend.config.js", "jest-backend": "cross-env TEST_BACKEND=1 jest --config=./config/jest-backend.config.js",
"tsc": "tsc", "tsc": "tsc",

View file

@ -79,7 +79,7 @@ class Database {
console.log(`Data Dir: ${Database.dataDir}`); console.log(`Data Dir: ${Database.dataDir}`);
} }
static async connect() { static async connect(testMode = false) {
const acquireConnectionTimeout = 120 * 1000; const acquireConnectionTimeout = 120 * 1000;
const Dialect = require("knex/lib/dialects/sqlite3/index.js"); const Dialect = require("knex/lib/dialects/sqlite3/index.js");
@ -112,8 +112,13 @@ class Database {
await R.autoloadModels("./server/model"); await R.autoloadModels("./server/model");
await R.exec("PRAGMA foreign_keys = ON"); await R.exec("PRAGMA foreign_keys = ON");
// Change to WAL if (testMode) {
await R.exec("PRAGMA journal_mode = WAL"); // Change to MEMORY
await R.exec("PRAGMA journal_mode = MEMORY");
} else {
// Change to WAL
await R.exec("PRAGMA journal_mode = WAL");
}
await R.exec("PRAGMA cache_size = -12000"); await R.exec("PRAGMA cache_size = -12000");
await R.exec("PRAGMA auto_vacuum = FULL"); await R.exec("PRAGMA auto_vacuum = FULL");

View file

@ -176,7 +176,7 @@ exports.entryPage = "dashboard";
(async () => { (async () => {
Database.init(args); Database.init(args);
await initDatabase(); await initDatabase(testMode);
exports.entryPage = await setting("entryPage"); exports.entryPage = await setting("entryPage");
@ -1417,14 +1417,14 @@ async function getMonitorJSONList(userID) {
return result; return result;
} }
async function initDatabase() { async function initDatabase(testMode = false) {
if (! fs.existsSync(Database.path)) { if (! fs.existsSync(Database.path)) {
console.log("Copying Database"); console.log("Copying Database");
fs.copyFileSync(Database.templatePath, Database.path); fs.copyFileSync(Database.templatePath, Database.path);
} }
console.log("Connecting to the Database"); console.log("Connecting to the Database");
await Database.connect(); await Database.connect(testMode);
console.log("Connected"); console.log("Connected");
// Patch the database // Patch the database

View file

@ -26,6 +26,7 @@
<div class="form-text mt-2 mb-4 ms-2">{{ $t("shrinkDatabaseDescription") }}</div> <div class="form-text mt-2 mb-4 ms-2">{{ $t("shrinkDatabaseDescription") }}</div>
</div> </div>
<button <button
id="clearAllStats-btn"
class="btn btn-outline-danger me-2 mb-2" class="btn btn-outline-danger me-2 mb-2"
@click="confirmClearStatistics" @click="confirmClearStatistics"
> >

View file

@ -5,7 +5,7 @@
<template v-if="!settings.disableAuth"> <template v-if="!settings.disableAuth">
<p> <p>
{{ $t("Current User") }}: <strong>{{ username }}</strong> {{ $t("Current User") }}: <strong>{{ username }}</strong>
<button v-if="! settings.disableAuth" class="btn btn-danger ms-4 me-2 mb-2" @click="$root.logout">{{ $t("Logout") }}</button> <button v-if="! settings.disableAuth" id="logout-btn" class="btn btn-danger ms-4 me-2 mb-2" @click="$root.logout">{{ $t("Logout") }}</button>
</p> </p>
<h5 class="my-4">{{ $t("Change Password") }}</h5> <h5 class="my-4">{{ $t("Change Password") }}</h5>
@ -81,8 +81,8 @@
<h5 class="my-4">{{ $t("Advanced") }}</h5> <h5 class="my-4">{{ $t("Advanced") }}</h5>
<div class="mb-4"> <div class="mb-4">
<button v-if="settings.disableAuth" class="btn btn-outline-primary me-2 mb-2" @click="enableAuth">{{ $t("Enable Auth") }}</button> <button v-if="settings.disableAuth" id="enableAuth-btn" class="btn btn-outline-primary me-2 mb-2" @click="enableAuth">{{ $t("Enable Auth") }}</button>
<button v-if="! settings.disableAuth" class="btn btn-primary me-2 mb-2" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button> <button v-if="! settings.disableAuth" id="disableAuth-btn" class="btn btn-primary me-2 mb-2" @click="confirmDisableAuth">{{ $t("Disable Auth") }}</button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -4,7 +4,7 @@
2. Create a language file (e.g. `zh-TW.js`). The filename must be ISO language code: http://www.lingoes.net/en/translator/langcode.htm 2. Create a language file (e.g. `zh-TW.js`). The filename must be ISO language code: http://www.lingoes.net/en/translator/langcode.htm
3. Run `npm run update-language-files`. You can also use this command to check if there are new strings to translate for your language. 3. Run `npm run update-language-files`. You can also use this command to check if there are new strings to translate for your language.
4. Your language file should be filled in. You can translate now. 4. Your language file should be filled in. You can translate now.
5. Translate `src/pages/Settings.vue` (search for a `Confirm` component with `rel="confirmDisableAuth"`). 5. Translate `src/components/settings/Security.vue` (search for a `Confirm` component with `rel="confirmDisableAuth"`).
6. Import your language file in `src/i18n.js` and add it to `languageList` constant. 6. Import your language file in `src/i18n.js` and add it to `languageList` constant.
7. Make a [pull request](https://github.com/louislam/uptime-kuma/pulls) when you have done. 7. Make a [pull request](https://github.com/louislam/uptime-kuma/pulls) when you have done.

View file

@ -194,7 +194,7 @@
</div> </div>
<div class="mt-5 mb-1"> <div class="mt-5 mb-1">
<button class="btn btn-primary" type="submit" :disabled="processing">{{ $t("Save") }}</button> <button id="monitor-submit-btn" class="btn btn-primary" type="submit" :disabled="processing">{{ $t("Save") }}</button>
</div> </div>
</div> </div>

View file

@ -59,18 +59,31 @@ describe("Init", () => {
// Go to / // Go to /
await page.goto(baseURL); await page.goto(baseURL);
await sleep(3000); await page.waitForSelector("h1.mb-3");
pathname = await page.evaluate(() => location.pathname); pathname = await page.evaluate(() => location.pathname);
expect(pathname).toEqual("/dashboard"); expect(pathname).toEqual("/dashboard");
}); });
it("should create monitor", async () => {
// Create monitor
await page.goto(baseURL + "/add");
await page.waitForSelector("#name");
await page.type("#name", "Myself");
await page.waitForSelector("#url");
await page.click("#url", { clickCount: 3 });
await page.keyboard.type(baseURL);
await page.keyboard.press("Enter");
});
// Settings Page // Settings Page
describe("Settings", () => { describe("Settings", () => {
beforeAll(async () => { beforeEach(async () => {
await page.goto(baseURL + "/settings"); await page.goto(baseURL + "/settings");
}); });
it("Change Language", async () => { it("Change Language", async () => {
await page.goto(baseURL + "/settings/appearance");
await page.waitForSelector("#language"); await page.waitForSelector("#language");
await page.select("#language", "zh-HK"); await page.select("#language", "zh-HK");
@ -83,20 +96,33 @@ describe("Init", () => {
}); });
it("Change Theme", async () => { it("Change Theme", async () => {
await sleep(1000); await page.goto(baseURL + "/settings/appearance");
// Dark // Dark
await click(page, ".btn[for=btncheck2]"); await click(page, ".btn[for=btncheck2]");
await page.waitForSelector("div.dark"); await page.waitForSelector("div.dark");
await sleep(1000); await page.waitForSelector(".btn[for=btncheck1]");
// Light // Light
await click(page, ".btn[for=btncheck1]"); await click(page, ".btn[for=btncheck1]");
await page.waitForSelector("div.light"); await page.waitForSelector("div.light");
}); });
// TODO: Heartbeat Bar Style it("Change Heartbeat Bar Style", async () => {
await page.goto(baseURL + "/settings/appearance");
// Bottom
await click(page, ".btn[for=btncheck5]");
await page.waitForSelector("div.hp-bar-big");
// None
await click(page, ".btn[for=btncheck6]");
await page.waitForSelector("div.hp-bar-big", {
hidden: true,
timeout: 1000
});
});
// TODO: Timezone // TODO: Timezone
@ -108,14 +134,14 @@ describe("Init", () => {
// Yes // Yes
await click(page, "#searchEngineIndexYes"); await click(page, "#searchEngineIndexYes");
await click(page, "form > div > .btn[type=submit]"); await click(page, "form > div > .btn[type=submit]");
await sleep(2000); await sleep(1000);
res = await axios.get(baseURL + "/robots.txt"); res = await axios.get(baseURL + "/robots.txt");
expect(res.data).not.toContain("Disallow: /"); expect(res.data).not.toContain("Disallow: /");
// No // No
await click(page, "#searchEngineIndexNo"); await click(page, "#searchEngineIndexNo");
await click(page, "form > div > .btn[type=submit]"); await click(page, "form > div > .btn[type=submit]");
await sleep(2000); await sleep(1000);
res = await axios.get(baseURL + "/robots.txt"); res = await axios.get(baseURL + "/robots.txt");
expect(res.data).toContain("Disallow: /"); expect(res.data).toContain("Disallow: /");
}); });
@ -125,25 +151,25 @@ describe("Init", () => {
// Default // Default
await newPage.goto(baseURL); await newPage.goto(baseURL);
await sleep(3000); await newPage.waitForSelector("h1.mb-3", { timeout: 3000 });
let pathname = await newPage.evaluate(() => location.pathname); let pathname = await newPage.evaluate(() => location.pathname);
expect(pathname).toEqual("/dashboard"); expect(pathname).toEqual("/dashboard");
// Status Page // Status Page
await click(page, "#entryPageNo"); await click(page, "#entryPageNo");
await click(page, "form > div > .btn[type=submit]"); await click(page, "form > div > .btn[type=submit]");
await sleep(4000); await sleep(1000);
await newPage.goto(baseURL); await newPage.goto(baseURL);
await sleep(4000); await newPage.waitForSelector("img.logo", { timeout: 3000 });
pathname = await newPage.evaluate(() => location.pathname); pathname = await newPage.evaluate(() => location.pathname);
expect(pathname).toEqual("/status"); expect(pathname).toEqual("/status");
// Back to Dashboard // Back to Dashboard
await click(page, "#entryPageYes"); await click(page, "#entryPageYes");
await click(page, "form > div > .btn[type=submit]"); await click(page, "form > div > .btn[type=submit]");
await sleep(4000); await sleep(1000);
await newPage.goto(baseURL); await newPage.goto(baseURL);
await sleep(4000); await newPage.waitForSelector("h1.mb-3", { timeout: 3000 });
pathname = await newPage.evaluate(() => location.pathname); pathname = await newPage.evaluate(() => location.pathname);
expect(pathname).toEqual("/dashboard"); expect(pathname).toEqual("/dashboard");
@ -151,7 +177,7 @@ describe("Init", () => {
}); });
it("Change Password (wrong current password)", async () => { it("Change Password (wrong current password)", async () => {
await page.goto(baseURL + "/settings"); await page.goto(baseURL + "/settings/security");
await page.waitForSelector("#current-password"); await page.waitForSelector("#current-password");
await page.type("#current-password", "wrong_passw$$d"); await page.type("#current-password", "wrong_passw$$d");
@ -159,10 +185,10 @@ describe("Init", () => {
await page.type("#repeat-new-password", "new_password123"); await page.type("#repeat-new-password", "new_password123");
// Save // Save
await click(page, "form > div > .btn[type=submit]", 1); await click(page, "form > div > .btn[type=submit]", 0);
await sleep(4000); await sleep(1000);
await click(page, ".btn-danger.btn.me-2"); await click(page, "#logout-btn");
await login("admin", "new_password123"); await login("admin", "new_password123");
let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length); let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);
expect(elementCount).toEqual(1); expect(elementCount).toEqual(1);
@ -171,24 +197,26 @@ describe("Init", () => {
}); });
it("Change Password (wrong repeat)", async () => { it("Change Password (wrong repeat)", async () => {
await page.goto(baseURL + "/settings"); await page.goto(baseURL + "/settings/security");
await page.waitForSelector("#current-password"); await page.waitForSelector("#current-password");
await page.type("#current-password", "admin123"); await page.type("#current-password", "admin123");
await page.type("#new-password", "new_password123"); await page.type("#new-password", "new_password123");
await page.type("#repeat-new-password", "new_password1234567898797898"); await page.type("#repeat-new-password", "new_password1234567898797898");
await click(page, "form > div > .btn[type=submit]", 1); await click(page, "form > div > .btn[type=submit]", 0);
await sleep(4000); await sleep(1000);
await click(page, ".btn-danger.btn.me-2"); await click(page, "#logout-btn");
await login("admin", "new_password123"); await login("admin", "new_password123");
let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length); let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length);
expect(elementCount).toEqual(1); expect(elementCount).toEqual(1);
await login("admin", "admin123"); await login("admin", "admin123");
await sleep(3000); await page.waitForSelector("#current-password");
let pathname = await page.evaluate(() => location.pathname);
expect(pathname).toEqual("/settings/security");
}); });
// TODO: 2FA // TODO: 2FA
@ -197,9 +225,35 @@ describe("Init", () => {
// TODO: Import Backup // TODO: Import Backup
// TODO: Disable Auth it("Should disable & enable auth", async () => {
await page.goto(baseURL + "/settings/security");
await click(page, "#disableAuth-btn");
await click(page, ".btn.btn-danger[data-bs-dismiss='modal']", 2); // Not a good way to do it
await page.waitForSelector("#enableAuth-btn", { timeout: 3000 });
await page.waitForSelector("#logout-btn", {
hidden: true,
timeout: 3000
});
// TODO: Clear Stats const newPage = await browser.newPage();
await newPage.goto(baseURL);
await newPage.waitForSelector("span.badge", { timeout: 3000 });
newPage.close();
await click(page, "#enableAuth-btn");
await login("admin", "admin123");
await page.waitForSelector("#disableAuth-btn", { timeout: 3000 });
});
it("Should clear all statistics", async () => {
await page.goto(baseURL + "/settings/monitor-history");
await click(page, "#clearAllStats-btn");
await click(page, ".btn.btn-danger");
await page.waitForFunction(() => {
const badge = document.querySelector("span.badge");
return badge && badge.innerText == "0%";
}, { timeout: 3000 });
});
}); });
/* /*