Skip to content

Commit

Permalink
more end to end tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sdumetz committed Feb 6, 2025
1 parent 0084670 commit cb55d95
Show file tree
Hide file tree
Showing 6 changed files with 390 additions and 59 deletions.
89 changes: 89 additions & 0 deletions source/e2e/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import path from "node:path";
import { expect, Page, test as base } from '@playwright/test';
import { randomBytes, randomUUID } from 'node:crypto';


const fixtures = path.resolve(import.meta.dirname, "./__test_fixtures");

export type CreateSceneOptions = {
permissions?: Record<string,null|"none"|"read"|"write">,
autoDelete?:boolean,
}

type TestFixture = {
adminPage:Page,
userPage:Page,
createScene:(opts?:CreateSceneOptions)=>Promise<string>,
uniqueAccount: {username:string, password:string, uid:number},

}

export {expect} from "@playwright/test";

export const test = base.extend<TestFixture>({
adminPage: async ({browser}, use)=>{
const ctx = await browser.newContext({ storageState: 'playwright/.auth/admin.json', locale: "cimode" });
const adminPage = await ctx.newPage();
await use(adminPage);
await ctx.close();
},
userPage: async ({browser}, use)=>{
const ctx = await browser.newContext({ storageState: 'playwright/.auth/user.json', locale: "cimode" });
const userPage = await ctx.newPage();
await use(userPage);
await ctx.close();
},
/**
* Factory function to create new scenes with random names
* Will _generally_ clean up scenes afterwards
*/
createScene: async ({browser}, use)=>{
const fs = await import("node:fs/promises");
const data = await fs.readFile(path.join(fixtures, "cube.glb"))
const ctx = await browser.newContext({ storageState: 'playwright/.auth/user.json', locale: "cimode" });
const request = ctx.request;
const names :string[] = [];
await use(async ({permissions, autoDelete=true} :CreateSceneOptions={})=>{
const name = randomUUID();
if(autoDelete) names.push(name);
let res = await request.post(`/scenes/${encodeURIComponent(name)}`, {
data,
headers: {"Content-Type": "model/gltf-binary"}
});
await expect(res).toBeOK();
//Set expected permissions
if(permissions){
res = await request.patch(`/scenes/${encodeURIComponent(name)}`, {
data: {
permissions: permissions
}
});
}
return name;
});
await Promise.all(names.map(name=> request.delete(`/scenes/${encodeURIComponent(name)}?archive=false`)));
await ctx.close();
},
uniqueAccount: async ({browser}, use)=>{
let username = `testUserLogin${randomBytes(2).readUInt16LE().toString(36)}`;
let password = randomBytes(16).toString("base64");
let adminContext = await browser.newContext({storageState: "playwright/.auth/admin.json"});
//Create a user for this specific test
let res = await adminContext.request.post("/users", {
data: JSON.stringify({
username,
email: `${username}@example.com`,
password,
isAdministrator: false,
}),
headers:{
"Content-Type": "application/json",
}
});
let body = JSON.parse(await res.text());
expect(body).toHaveProperty("uid");
let uid :number =body.uid;
await use({username, password, uid});
await adminContext.close();
},
});
94 changes: 94 additions & 0 deletions source/e2e/tests/admin.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import path from "node:path";


import { expect, test } from '@playwright/test';
import { randomUUID } from "node:crypto";

const fixtures = path.resolve(import.meta.dirname, "../__test_fixtures");

//Authenticated as admin
test.use({ storageState: 'playwright/.auth/admin.json', locale: "cimode" });



test.skip("can create a new user", async ({page})=>{
await page.goto("/ui/admin/users");

});

test.skip("can delete a non-admin user", async ({page})=>{

});

test("can force-delete archived scenes", async ({page, request})=>{

const name = randomUUID();
const fs = await import("node:fs/promises");
const data = await fs.readFile(path.join(fixtures, "cube.glb"))
let res = await page.request.post(`/scenes/${encodeURIComponent(name)}`, {
data,
headers: {"Content-Type": "model/gltf-binary"}
});
await expect(res).toBeOK();

res = await page.request.delete(`/scenes/${encodeURIComponent(name)}?archive=true`);
await expect(res).toBeOK();

res = await request.get(`/scenes?archived=any&match=${name}`);
let body = await res.json();
expect(body.scenes).toHaveLength(1);
expect(body.scenes[0]).toHaveProperty("archived", true);

//There can be any number of archived scenes shown here that leaks from other tests
//But we expect "our" scene to be there and that's what we are looking for
await page.goto("/ui/admin/archives");

await expect(page.getByRole("link", {name})).toBeVisible();

await page.getByRole("row", {name}).getByRole("button", {name: "labels.delete"}).click();


await expect(page.getByRole("link", {name})).not.toBeVisible();


res = await request.get(`/scenes?archived=any&match=${name}`);
body = await res.json();
expect(body.scenes).toHaveLength(0);
});

test("can restore archived scenes", async ({page, request})=>{

const name = randomUUID();
const fs = await import("node:fs/promises");
const data = await fs.readFile(path.join(fixtures, "cube.glb"))
let res = await page.request.post(`/scenes/${encodeURIComponent(name)}`, {
data,
headers: {"Content-Type": "model/gltf-binary"}
});
await expect(res).toBeOK();

res = await page.request.delete(`/scenes/${encodeURIComponent(name)}?archive=true`);
await expect(res).toBeOK();

res = await request.get(`/scenes?archived=any&match=${name}`);
let body = await res.json();
expect(body.scenes).toHaveLength(1);
expect(body.scenes[0]).toHaveProperty("archived", true);

//There can be any number of archived scenes shown here that leaks from other tests
//But we expect "our" scene to be there and that's what we are looking for
await page.goto("/ui/admin/archives");

await expect(page.getByRole("link", {name})).toBeVisible();

await page.getByRole("row", {name}).getByRole("button", {name: "labels.restore"}).click();


await expect(page.getByRole("link", {name})).not.toBeVisible();


res = await request.get(`/scenes?archived=any&match=${name}`);
body = await res.json();
expect(body.scenes).toHaveLength(1);
expect(body.scenes[0]).toHaveProperty("archived", false);
});
21 changes: 0 additions & 21 deletions source/e2e/tests/admin.test.ts

This file was deleted.

64 changes: 64 additions & 0 deletions source/e2e/tests/history.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import path from "node:path";
import fs from "node:fs/promises";
import { randomUUID } from "node:crypto";

import { test, expect, Page } from '@playwright/test';

const fixtures = path.resolve(import.meta.dirname, "../__test_fixtures");

//Authenticated as user
test.use({ storageState: 'playwright/.auth/user.json', locale: "cimode" });
test.describe.configure({ mode: 'serial' });

let scenePage: Page;

const name = randomUUID();


let initial_doc :string;
/**
* Tests in this suite are run in serial mode with no page reload, unless otherwise specified
*/
test.beforeAll(async ({request, browser})=>{
//Create a scene
let res = await request.post(`/scenes/${encodeURIComponent(name)}?language=FR`, {
data: await fs.readFile(path.join(fixtures, "cube.glb")),
headers: {"Content-Type": "model/gltf-binary"}
});
await expect(res).toBeOK();

res = await request.get(`/scenes/${encodeURIComponent(name)}/scene.svx.json`);
initial_doc = await res.text();

expect(initial_doc).toBeTruthy();
expect(initial_doc.slice(0,1)).toEqual("{"); //Checks that it appears to be JSON...

//Make a bunch of changes
res = await request.put(`/scenes/${encodeURIComponent(name)}/articles/new-article-OKiTjtY6zrbJ-EN.html`, {
data: await fs.readFile(path.join(fixtures, "new-article-OKiTjtY6zrbJ-EN.html")),
headers: {"Content-Type": "text/html"}
});
await expect(res).toBeOK();

res = await request.put(`/scenes/${encodeURIComponent(name)}/scene.svx.json`, {
data: await fs.readFile(path.join(fixtures, "scene.svx.json")),
headers: {"Content-Type": "application/json"}
});
await expect(res).toBeOK();


scenePage = await browser.newPage();
await scenePage.goto(`/ui/scenes/${name}`);
});


test.afterAll(async () => {
await scenePage.close();
});


test("can navigate to history page", async ()=>{
await scenePage.getByRole("link", {name: "buttons.history"}).click();
await scenePage.waitForURL(`/ui/scenes/${name}/history`);

});
92 changes: 92 additions & 0 deletions source/e2e/tests/scene_settings.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import path from "node:path";
import fs from "node:fs/promises";


import { expect, test } from '../fixtures';
import { randomUUID } from "node:crypto";



test.use({ storageState: {cookies:[], origins: []}, locale: "cimode"});


//Authenticated as user


test.describe("author", ()=>{

test("can edit his scenes", async ({userPage, createScene})=>{
let name = await createScene();
await userPage.goto(`/ui/scenes/${encodeURIComponent(name)}`);

//Expect "edit" and "history" buttons to be present
await expect(userPage.getByRole("link", {name: "labels.edit", exact: true})).toHaveAttribute("href", `/ui/scenes/${encodeURIComponent(name)}/edit`)
await expect(userPage.getByRole("link", {name: "buttons.history", exact: true})).toHaveAttribute("href", `/ui/scenes/${encodeURIComponent(name)}/history`)
});

test("can archive and restore his scenes", async ({userPage, createScene})=>{
let name = await createScene();
await userPage.goto(`/ui/scenes/${encodeURIComponent(name)}`);
await userPage.getByRole('button', { name: 'buttons.archive' }).click();

//We are now on the archived scene's page
await userPage.waitForURL(/\/ui\/scenes\/.+/);

//Check it is truly archived
let res = await userPage.request.get(`/ui/scenes/${encodeURIComponent(name)}`);
expect(res.status()).toEqual(404);

//Default rename target should be our original name
await expect(userPage.locator("#scene-name-input")).toHaveValue(name);
//Decide of a new name for the scene
let newName = randomUUID();
await userPage.locator("#scene-name-input").fill(newName);
await userPage.getByRole("button", {name: "labels.restore"}).click();

await userPage.waitForURL(`/ui/scenes/${newName}`);
});
});

test.describe("admin", ()=>{
test("can edit other users' scenes", async ({adminPage, createScene})=>{
let name = await createScene();
await adminPage.goto(`/ui/scenes/${encodeURIComponent(name)}`);

//Expect "edit" and "history" buttons to be present
await expect(adminPage.getByRole("link", {name: "labels.edit", exact: true})).toHaveAttribute("href", `/ui/scenes/${encodeURIComponent(name)}/edit`)
await expect(adminPage.getByRole("link", {name: "buttons.history", exact: true})).toHaveAttribute("href", `/ui/scenes/${encodeURIComponent(name)}/history`)
});

test("can archive other user's scene", async ({adminPage, createScene})=>{
let name = await createScene();
await adminPage.goto(`/ui/scenes/${encodeURIComponent(name)}`);
await adminPage.getByRole('button', { name: 'buttons.archive' }).click()
await adminPage.waitForURL(new RegExp(`/ui/scenes/${name}`));

let res = await adminPage.request.get(`/ui/scenes/${encodeURIComponent(name)}`);
expect(res.status()).toEqual(404);
});

});

test("read-only view", async ({page, createScene})=>{
let name = await createScene();
await page.goto(`/ui/scenes/${encodeURIComponent(name)}`);

//Expect "edit" and "history" buttons to be present
await expect(page.getByRole("link", {name: "labels.view", exact: true})).toBeVisible();
await expect(page.getByRole("link", {name: "labels.edit", exact: true})).not.toBeVisible();
await expect(page.getByRole("link", {name: "buttons.history", exact: true})).not.toBeVisible();

let res= await page.goto(`/ui/scenes/${encodeURIComponent(name)}/edit`);
expect(res?.status()).toEqual(404);
});


test("404 view", async ({page, createScene})=>{
let name = await createScene({permissions: {default: "none"}});
for (let p of ["", "edit", "view", "history"]) {
let res= await page.goto(`/ui/scenes/${encodeURIComponent(name)}/${p}`);
expect(res?.status()).toEqual(404);
}
});
Loading

0 comments on commit cb55d95

Please sign in to comment.