37 changed files with 2007 additions and 2112 deletions
			
			
		@ -0,0 +1,34 @@ | 
				
			|||
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node | 
				
			|||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions | 
				
			|||
 | 
				
			|||
name: Auto Test | 
				
			|||
 | 
				
			|||
on: | 
				
			|||
  push: | 
				
			|||
    branches: [ master ] | 
				
			|||
  pull_request: | 
				
			|||
    branches: [ master ] | 
				
			|||
 | 
				
			|||
jobs: | 
				
			|||
  build: | 
				
			|||
    runs-on: ubuntu-latest | 
				
			|||
 | 
				
			|||
    strategy: | 
				
			|||
      matrix: | 
				
			|||
        node-version: [14.x, 15.x, 16.x] | 
				
			|||
        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ | 
				
			|||
 | 
				
			|||
    steps: | 
				
			|||
    - uses: actions/checkout@v2 | 
				
			|||
 | 
				
			|||
    - name: Use Node.js ${{ matrix.node-version }} | 
				
			|||
      uses: actions/setup-node@v2 | 
				
			|||
      with: | 
				
			|||
        node-version: ${{ matrix.node-version }} | 
				
			|||
        cache: 'npm' | 
				
			|||
    - run: npm ci | 
				
			|||
    - run: npm run build | 
				
			|||
    - run: npm test | 
				
			|||
      env: | 
				
			|||
        HEADLESS_TEST: 1 | 
				
			|||
        JUST_FOR_TEST: ${{ secrets.JUST_FOR_TEST }} | 
				
			|||
@ -1,5 +1,6 @@ | 
				
			|||
module.exports = { | 
				
			|||
    "launch": { | 
				
			|||
        "headless": false | 
				
			|||
        "headless": process.env.HEADLESS_TEST || false, | 
				
			|||
        "userDataDir": "./data/test-chrome-profile", | 
				
			|||
    } | 
				
			|||
}; | 
				
			|||
 | 
				
			|||
								
									
										File diff suppressed because it is too large
									
								
							
						
					@ -0,0 +1,9 @@ | 
				
			|||
const fs = require("fs"); | 
				
			|||
 | 
				
			|||
const path = "./data/test-chrome-profile"; | 
				
			|||
 | 
				
			|||
if (fs.existsSync(path)) { | 
				
			|||
    fs.rmdirSync(path, { | 
				
			|||
        recursive: true, | 
				
			|||
    }); | 
				
			|||
} | 
				
			|||
@ -0,0 +1,9 @@ | 
				
			|||
const fs = require("fs"); | 
				
			|||
 | 
				
			|||
const path = "./data/test"; | 
				
			|||
 | 
				
			|||
if (fs.existsSync(path)) { | 
				
			|||
    fs.rmdirSync(path, { | 
				
			|||
        recursive: true, | 
				
			|||
    }); | 
				
			|||
} | 
				
			|||
@ -1,21 +1,236 @@ | 
				
			|||
beforeAll(() => { | 
				
			|||
// eslint-disable-next-line no-unused-vars
 | 
				
			|||
const { Page, Browser } = require("puppeteer"); | 
				
			|||
const { sleep } = require("../src/util"); | 
				
			|||
const axios = require("axios"); | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * Set back the correct data type for page object | 
				
			|||
 * @type {Page} | 
				
			|||
 */ | 
				
			|||
page; | 
				
			|||
 | 
				
			|||
/** | 
				
			|||
 * @type {Browser} | 
				
			|||
 */ | 
				
			|||
browser; | 
				
			|||
 | 
				
			|||
beforeAll(async () => { | 
				
			|||
    await page.setViewport({ | 
				
			|||
        width: 1280, | 
				
			|||
        height: 720, | 
				
			|||
        deviceScaleFactor: 1, | 
				
			|||
    }); | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
afterAll(() => { | 
				
			|||
    return console.log("Cleanup"); | 
				
			|||
 | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
describe("Very Simple Test", () => { | 
				
			|||
const baseURL = "http://127.0.0.1:3002"; | 
				
			|||
 | 
				
			|||
describe("Init", () => { | 
				
			|||
    const title = "Uptime Kuma"; | 
				
			|||
 | 
				
			|||
    beforeAll(async () => { | 
				
			|||
        await page.goto("http://127.0.0.1:3002"); | 
				
			|||
        await page.goto(baseURL); | 
				
			|||
    }); | 
				
			|||
 | 
				
			|||
    it(`should be titled "${title}"`, async () => { | 
				
			|||
        await expect(page.title()).resolves.toMatch(title); | 
				
			|||
    }); | 
				
			|||
 | 
				
			|||
    // Setup Page
 | 
				
			|||
    it("Setup", async () => { | 
				
			|||
        // Create an Admin
 | 
				
			|||
        await page.waitForSelector("#floatingInput"); | 
				
			|||
        await page.waitForSelector("#repeat"); | 
				
			|||
        await page.click("#floatingInput"); | 
				
			|||
        await page.type("#floatingInput", "admin"); | 
				
			|||
        await page.type("#floatingPassword", "admin123"); | 
				
			|||
        await page.type("#repeat", "admin123"); | 
				
			|||
        await page.click(".btn-primary[type=submit]"); | 
				
			|||
        await sleep(3000); | 
				
			|||
 | 
				
			|||
        // Go to /setup again
 | 
				
			|||
        await page.goto(baseURL + "/setup"); | 
				
			|||
        await sleep(3000); | 
				
			|||
        let pathname = await page.evaluate(() => location.pathname); | 
				
			|||
        expect(pathname).toEqual("/dashboard"); | 
				
			|||
 | 
				
			|||
        // Go to /
 | 
				
			|||
        await page.goto(baseURL); | 
				
			|||
        await sleep(3000); | 
				
			|||
        pathname = await page.evaluate(() => location.pathname); | 
				
			|||
        expect(pathname).toEqual("/dashboard"); | 
				
			|||
    }); | 
				
			|||
 | 
				
			|||
    // Settings Page
 | 
				
			|||
    describe("Settings", () => { | 
				
			|||
        beforeAll(async () => { | 
				
			|||
            await page.goto(baseURL + "/settings"); | 
				
			|||
        }); | 
				
			|||
 | 
				
			|||
        it("Change Language", async () => { | 
				
			|||
            await page.waitForSelector("#language"); | 
				
			|||
 | 
				
			|||
            await page.select("#language", "zh-HK"); | 
				
			|||
            let languageTitle = await page.evaluate(() => document.querySelector("[for=language]").innerText); | 
				
			|||
            expect(languageTitle).toMatch("語言"); | 
				
			|||
 | 
				
			|||
            await page.select("#language", "en"); | 
				
			|||
            languageTitle = await page.evaluate(() => document.querySelector("[for=language]").innerText); | 
				
			|||
            expect(languageTitle).toMatch("Language"); | 
				
			|||
        }); | 
				
			|||
 | 
				
			|||
        it("Change Theme", async () => { | 
				
			|||
            await sleep(1000); | 
				
			|||
 | 
				
			|||
            // Dark
 | 
				
			|||
            await click(page, ".btn[for=btncheck2]"); | 
				
			|||
            await page.waitForSelector("div.dark"); | 
				
			|||
 | 
				
			|||
            await sleep(1000); | 
				
			|||
 | 
				
			|||
            // Light
 | 
				
			|||
            await click(page, ".btn[for=btncheck1]"); | 
				
			|||
            await page.waitForSelector("div.light"); | 
				
			|||
        }); | 
				
			|||
 | 
				
			|||
        // TODO: Heartbeat Bar Style
 | 
				
			|||
 | 
				
			|||
        // TODO: Timezone
 | 
				
			|||
 | 
				
			|||
        it("Search Engine Visibility", async () => { | 
				
			|||
            // Default
 | 
				
			|||
            let res = await axios.get(baseURL + "/robots.txt"); | 
				
			|||
            expect(res.data).toMatch("Disallow: /"); | 
				
			|||
 | 
				
			|||
            // Yes
 | 
				
			|||
            await click(page, "#searchEngineIndexYes"); | 
				
			|||
            await click(page, "form > div > .btn[type=submit]"); | 
				
			|||
            await sleep(2000); | 
				
			|||
            res = await axios.get(baseURL + "/robots.txt"); | 
				
			|||
            expect(res.data).not.toMatch("Disallow: /"); | 
				
			|||
 | 
				
			|||
            // No
 | 
				
			|||
            await click(page, "#searchEngineIndexNo"); | 
				
			|||
            await click(page, "form > div > .btn[type=submit]"); | 
				
			|||
            await sleep(2000); | 
				
			|||
            res = await axios.get(baseURL + "/robots.txt"); | 
				
			|||
            expect(res.data).toMatch("Disallow: /"); | 
				
			|||
        }); | 
				
			|||
 | 
				
			|||
        it("Entry Page", async () => { | 
				
			|||
            const newPage = await browser.newPage(); | 
				
			|||
 | 
				
			|||
            // Default
 | 
				
			|||
            await newPage.goto(baseURL); | 
				
			|||
            await sleep(3000); | 
				
			|||
            let pathname = await newPage.evaluate(() => location.pathname); | 
				
			|||
            expect(pathname).toEqual("/dashboard"); | 
				
			|||
 | 
				
			|||
            // Status Page
 | 
				
			|||
            await click(page, "#entryPageNo"); | 
				
			|||
            await click(page, "form > div > .btn[type=submit]"); | 
				
			|||
            await sleep(2000); | 
				
			|||
            await newPage.goto(baseURL); | 
				
			|||
            await sleep(3000); | 
				
			|||
            pathname = await newPage.evaluate(() => location.pathname); | 
				
			|||
            expect(pathname).toEqual("/status"); | 
				
			|||
 | 
				
			|||
            // Back to Dashboard
 | 
				
			|||
            await click(page, "#entryPageYes"); | 
				
			|||
            await click(page, "form > div > .btn[type=submit]"); | 
				
			|||
            await sleep(2000); | 
				
			|||
            await newPage.goto(baseURL); | 
				
			|||
            await sleep(3000); | 
				
			|||
            pathname = await newPage.evaluate(() => location.pathname); | 
				
			|||
            expect(pathname).toEqual("/dashboard"); | 
				
			|||
 | 
				
			|||
            await newPage.close(); | 
				
			|||
        }); | 
				
			|||
 | 
				
			|||
        it("Change Password (wrong current password)", async () => { | 
				
			|||
            await page.type("#current-password", "wrong_passw$$d"); | 
				
			|||
            await page.type("#new-password", "new_password123"); | 
				
			|||
            await page.type("#repeat-new-password", "new_password123"); | 
				
			|||
            await click(page, "form > div > .btn[type=submit]", 1); | 
				
			|||
            await sleep(3000); | 
				
			|||
            await click(page, ".btn-danger.btn.me-1"); | 
				
			|||
            await sleep(2000); | 
				
			|||
            await login("admin", "new_password123"); | 
				
			|||
            await sleep(2000); | 
				
			|||
            let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length); | 
				
			|||
            expect(elementCount).toEqual(1); | 
				
			|||
 | 
				
			|||
            await login("admin", "admin123"); | 
				
			|||
            await sleep(3000); | 
				
			|||
        }); | 
				
			|||
 | 
				
			|||
        it("Change Password (wrong repeat)", async () => { | 
				
			|||
            await page.type("#current-password", "admin123"); | 
				
			|||
            await page.type("#new-password", "new_password123"); | 
				
			|||
            await page.type("#repeat-new-password", "new_password1234567898797898"); | 
				
			|||
            await click(page, "form > div > .btn[type=submit]", 1); | 
				
			|||
            await sleep(3000); | 
				
			|||
            await click(page, ".btn-danger.btn.me-1"); | 
				
			|||
            await sleep(2000); | 
				
			|||
            await login("admin", "new_password123"); | 
				
			|||
            await sleep(2000); | 
				
			|||
            let elementCount = await page.evaluate(() => document.querySelectorAll("#floatingPassword").length); | 
				
			|||
            expect(elementCount).toEqual(1); | 
				
			|||
 | 
				
			|||
            await login("admin", "admin123"); | 
				
			|||
            await sleep(3000); | 
				
			|||
        }); | 
				
			|||
 | 
				
			|||
        // TODO: 2FA
 | 
				
			|||
 | 
				
			|||
        // TODO: Export Backup
 | 
				
			|||
 | 
				
			|||
        // TODO: Import Backup
 | 
				
			|||
 | 
				
			|||
        // TODO: Disable Auth
 | 
				
			|||
 | 
				
			|||
        // TODO: Clear Stats
 | 
				
			|||
    }); | 
				
			|||
 | 
				
			|||
    /* | 
				
			|||
     * TODO | 
				
			|||
     * Create Monitor - All type | 
				
			|||
     * Edit Monitor | 
				
			|||
     * Delete Monitor | 
				
			|||
     * | 
				
			|||
     * Create Notification (token problem, maybe hard to test) | 
				
			|||
     * | 
				
			|||
     */ | 
				
			|||
 | 
				
			|||
    describe("Status Page", () => { | 
				
			|||
        const title = "Uptime Kuma"; | 
				
			|||
        beforeAll(async () => { | 
				
			|||
            await page.goto(baseURL + "/status"); | 
				
			|||
        }); | 
				
			|||
        it(`should be titled "${title}"`, async () => { | 
				
			|||
            await expect(page.title()).resolves.toMatch(title); | 
				
			|||
        }); | 
				
			|||
    }); | 
				
			|||
}); | 
				
			|||
 | 
				
			|||
async function login(username, password) { | 
				
			|||
    await input(page, "#floatingInput", username); | 
				
			|||
    await input(page, "#floatingPassword", password); | 
				
			|||
    await page.click(".btn-primary[type=submit]"); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
async function click(page, selector, elementIndex = 0) { | 
				
			|||
    return await page.evaluate((s, i) => { | 
				
			|||
        return document.querySelectorAll(s)[i].click(); | 
				
			|||
    }, selector, elementIndex); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
async function input(page, selector, text) { | 
				
			|||
    const element = await page.$(selector); | 
				
			|||
    await element.click({ clickCount: 3 }); | 
				
			|||
    await page.keyboard.press("Backspace"); | 
				
			|||
    await page.type(selector, text); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
					Loading…
					
					
				
		Reference in new issue