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 = { | 
					module.exports = { | 
				
			||||
    "launch": { | 
					    "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(() => { | 
					afterAll(() => { | 
				
			||||
    return console.log("Cleanup"); | 
					
 | 
				
			||||
}); | 
					}); | 
				
			||||
 | 
					
 | 
				
			||||
describe("Very Simple Test", () => { | 
					const baseURL = "http://127.0.0.1:3002"; | 
				
			||||
 | 
					
 | 
				
			||||
 | 
					describe("Init", () => { | 
				
			||||
    const title = "Uptime Kuma"; | 
					    const title = "Uptime Kuma"; | 
				
			||||
 | 
					
 | 
				
			||||
    beforeAll(async () => { | 
					    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 () => { | 
					        it(`should be titled "${title}"`, async () => { | 
				
			||||
            await expect(page.title()).resolves.toMatch(title); | 
					            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