Skip to content

Add mobile playwright testing #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ jobs:
node-version: lts/*
- name: Install dependencies
run: npm ci

- name: Install Playwright Browsers
run: npx playwright install --with-deps

- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}

- name: Upload failed screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
name: failed-snapshots
path: test-results/
retention-days: 30
16 changes: 6 additions & 10 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,19 @@ export default defineConfig({
use: { ...devices["Desktop Safari"] },
},

/*
TODO: re-enable when we understand how to direct tests for mobile (for instance some element worth checking on desktop might be hidden on mobile and visa versa).
{
name: 'Mobile Safari',
use: { ...devices['iPhone 12'] },
name: "Mobile Safari",
use: { ...devices["iPhone 12"] },
},

{
name: 'Mobile Safari',
use: { ...devices['iPhone 14'] },
name: "Mobile Safari",
use: { ...devices["iPhone 14"] },
},

{
name: 'Mobile Chrome',
use: { ...devices['Pixel 5'] },
name: "Mobile Chrome",
use: { ...devices["Pixel 5"] },
},
*/
],

webServer: {
Expand Down
34 changes: 26 additions & 8 deletions tests/e2e/NewOrgModal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,22 @@
await API.getAccount(page, "NOT_SIGNED_IN");
});

test("can *NOT* see the 'New Org' button", async ({ page }) => {
test("can *NOT* see the 'New Org' button", async ({ page, isMobile }) => {
const response = await page.goto("http://localhost:1234");
expect(response?.status()).toBeLessThan(400);
await expect(page).toHaveScreenshot("no-new-org-button.png", {

Check failure on line 17 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[chromium] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button

1) [chromium] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/no-new-org-button-chromium-linux.png, writing actual. 15 | const response = await page.goto("http://localhost:1234"); 16 | expect(response?.status()).toBeLessThan(400); > 17 | await expect(page).toHaveScreenshot("no-new-org-button.png", { | ^ 18 | maxDiffPixels: 100, 19 | }); 20 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:17:5

Check failure on line 17 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[firefox] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button

3) [firefox] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/no-new-org-button-firefox-linux.png, writing actual. 15 | const response = await page.goto("http://localhost:1234"); 16 | expect(response?.status()).toBeLessThan(400); > 17 | await expect(page).toHaveScreenshot("no-new-org-button.png", { | ^ 18 | maxDiffPixels: 100, 19 | }); 20 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:17:5

Check failure on line 17 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[webkit] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button

5) [webkit] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/no-new-org-button-webkit-linux.png, writing actual. 15 | const response = await page.goto("http://localhost:1234"); 16 | expect(response?.status()).toBeLessThan(400); > 17 | await expect(page).toHaveScreenshot("no-new-org-button.png", { | ^ 18 | maxDiffPixels: 100, 19 | }); 20 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:17:5

Check failure on line 17 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[Mobile Safari] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button

7) [Mobile Safari] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/no-new-org-button-Mobile-Safari-linux.png, writing actual. 15 | const response = await page.goto("http://localhost:1234"); 16 | expect(response?.status()).toBeLessThan(400); > 17 | await expect(page).toHaveScreenshot("no-new-org-button.png", { | ^ 18 | maxDiffPixels: 100, 19 | }); 20 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:17:5

Check failure on line 17 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[Mobile Chrome] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button

9) [Mobile Chrome] › tests/e2e/NewOrgModal.spec.ts:14:7 › without being signed in › can *NOT* see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/no-new-org-button-Mobile-Chrome-linux.png, writing actual. 15 | const response = await page.goto("http://localhost:1234"); 16 | expect(response?.status()).toBeLessThan(400); > 17 | await expect(page).toHaveScreenshot("no-new-org-button.png", { | ^ 18 | maxDiffPixels: 100, 19 | }); 20 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:17:5
maxDiffPixels: 100,
});

await expect(
page.locator(".signed-in-nav_desktop .button").getByText("New Org"),
).not.toBeVisible();
if (isMobile) {
await expect(
page.locator(".signed-in-nav_mobile .button").getByText("New Org"),
).not.toBeVisible();
} else {
await expect(
page.locator(".signed-in-nav_desktop .button").getByText("New Org"),
).not.toBeVisible();
}
});
});

Expand All @@ -26,12 +35,21 @@
await API.getAccount(page, "@alice", { isSuperadmin: true });
});

test("can see the 'New Org' button", async ({ page }) => {
test("can see the 'New Org' button", async ({ page, isMobile }) => {
const response = await page.goto("http://localhost:1234");
expect(response?.status()).toBeLessThan(400);
await expect(page).toHaveScreenshot("new-org-button.png", {

Check failure on line 41 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[chromium] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button

2) [chromium] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/new-org-button-chromium-linux.png, writing actual. 39 | const response = await page.goto("http://localhost:1234"); 40 | expect(response?.status()).toBeLessThan(400); > 41 | await expect(page).toHaveScreenshot("new-org-button.png", { | ^ 42 | maxDiffPixels: 100, 43 | }); 44 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:41:5

Check failure on line 41 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[firefox] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button

4) [firefox] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/new-org-button-firefox-linux.png, writing actual. 39 | const response = await page.goto("http://localhost:1234"); 40 | expect(response?.status()).toBeLessThan(400); > 41 | await expect(page).toHaveScreenshot("new-org-button.png", { | ^ 42 | maxDiffPixels: 100, 43 | }); 44 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:41:5

Check failure on line 41 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[webkit] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button

6) [webkit] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/new-org-button-webkit-linux.png, writing actual. 39 | const response = await page.goto("http://localhost:1234"); 40 | expect(response?.status()).toBeLessThan(400); > 41 | await expect(page).toHaveScreenshot("new-org-button.png", { | ^ 42 | maxDiffPixels: 100, 43 | }); 44 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:41:5

Check failure on line 41 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[Mobile Safari] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button

8) [Mobile Safari] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/new-org-button-Mobile-Safari-linux.png, writing actual. 39 | const response = await page.goto("http://localhost:1234"); 40 | expect(response?.status()).toBeLessThan(400); > 41 | await expect(page).toHaveScreenshot("new-org-button.png", { | ^ 42 | maxDiffPixels: 100, 43 | }); 44 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:41:5

Check failure on line 41 in tests/e2e/NewOrgModal.spec.ts

View workflow job for this annotation

GitHub Actions / test

[Mobile Chrome] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button

10) [Mobile Chrome] › tests/e2e/NewOrgModal.spec.ts:38:7 › while being signed in › can see the 'New Org' button Error: A snapshot doesn't exist at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts-snapshots/new-org-button-Mobile-Chrome-linux.png, writing actual. 39 | const response = await page.goto("http://localhost:1234"); 40 | expect(response?.status()).toBeLessThan(400); > 41 | await expect(page).toHaveScreenshot("new-org-button.png", { | ^ 42 | maxDiffPixels: 100, 43 | }); 44 | at /home/runner/work/share-ui/share-ui/tests/e2e/NewOrgModal.spec.ts:41:5
maxDiffPixels: 100,
});

await expect(
page.locator(".signed-in-nav_desktop .button").getByText("New Org"),
).toBeVisible();
if (isMobile) {
await expect(
page.locator(".signed-in-nav_mobile .button").getByText("New Org"),
).toBeVisible();
} else {
await expect(
page.locator(".signed-in-nav_desktop .button").getByText("New Org"),
).toBeVisible();
}
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions tests/e2e/OrgPeoplePage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ test.describe("with org:manage permission", () => {
await API.getAccount(page, "@alice");
});

test("can view the org people page", async ({ page }) => {
test("can view the org people page", async ({ page, isMobile }) => {
const orgHandle = "@unison";
await API.getOrgProfile(page, orgHandle, { permissions: ["org:manage"] });
await API.getOrgRoleAssignments(page, orgHandle);
Expand All @@ -73,7 +73,11 @@ test.describe("with org:manage permission", () => {
const projects = page.locator(".page-content .profile-snippet");
await expect(projects).toHaveCount(3);

await expect(navItem(page, "People")).toBeVisible();
// The nav is hidden on mobile
if (!isMobile) {
await expect(navItem(page, "People")).toBeVisible();
}

await expect(button(page, "Add an organization member")).toBeVisible();
});
});
72 changes: 45 additions & 27 deletions tests/e2e/ProjectOverviewPage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ test.describe("without being signed in", () => {
await API.getAccount(page, "NOT_SIGNED_IN");
});

test("can view public project", async ({ page }) => {
test("can view public project", async ({ page, isMobile }) => {
await API.getProject(page, "@unison/base");

const response = await page.goto("http://localhost:1234/@unison/base");
Expand All @@ -25,13 +25,17 @@ test.describe("without being signed in", () => {

await expect(button(page, "Browse Project Code")).toBeVisible();
await expect(button(page, "Edit summary")).not.toBeVisible();
await expect(navItem(page, "Code")).toBeVisible();
await expect(navItem(page, "Tickets")).toBeVisible();
await expect(navItem(page, "Contributions")).toBeVisible();
await expect(navItem(page, "Settings")).not.toBeVisible();

// Nav is hidden on mobile
if (!isMobile) {
await expect(navItem(page, "Code")).toBeVisible();
await expect(navItem(page, "Tickets")).toBeVisible();
await expect(navItem(page, "Contributions")).toBeVisible();
await expect(navItem(page, "Settings")).not.toBeVisible();
}
});

test("can *not* view a private project`", async ({ page }) => {
test("can *not* view a private project`", async ({ page, isMobile }) => {
await API.getProject_(page, "@bob/private-project", { status: 404 });

const response = await page.goto(
Expand All @@ -43,10 +47,14 @@ test.describe("without being signed in", () => {
await expect(
page.getByText("Couldn't find @bob/private-project"),
).toBeVisible();
await expect(navItem(page, "Code")).not.toBeVisible();
await expect(navItem(page, "Tickets")).not.toBeVisible();
await expect(navItem(page, "Contributions")).not.toBeVisible();
await expect(navItem(page, "Settings")).not.toBeVisible();

// Nav is hidden on mobile
if (!isMobile) {
await expect(navItem(page, "Code")).not.toBeVisible();
await expect(navItem(page, "Tickets")).not.toBeVisible();
await expect(navItem(page, "Contributions")).not.toBeVisible();
await expect(navItem(page, "Settings")).not.toBeVisible();
}
});
});

Expand All @@ -60,6 +68,7 @@ test.describe("while signed in", () => {
test.describe("with an another user's private project and `project:view` permission", () => {
test("can view, but *not* edit summary or see settings", async ({
page,
isMobile,
}) => {
await API.getProject(page, "@bob/private-project", {
visibility: "private",
Expand All @@ -71,17 +80,21 @@ test.describe("while signed in", () => {
);
expect(response?.status()).toBeLessThan(400);

await expect(button(page, "Edit summary")).not.toBeVisible();
await expect(navItem(page, "Code")).toBeVisible();
await expect(navItem(page, "Tickets")).toBeVisible();
await expect(navItem(page, "Contributions")).toBeVisible();
await expect(navItem(page, "Settings")).not.toBeVisible();
// Nav is hidden on mobile
if (!isMobile) {
await expect(button(page, "Edit summary")).not.toBeVisible();
await expect(navItem(page, "Code")).toBeVisible();
await expect(navItem(page, "Tickets")).toBeVisible();
await expect(navItem(page, "Contributions")).toBeVisible();
await expect(navItem(page, "Settings")).not.toBeVisible();
}
});
});

test.describe("with an another user's private project and `project:maintain` permission", () => {
test("can view and edit summary, but not see settings", async ({
page,
isMobile,
}) => {
await API.getProject(page, "@bob/private-project", {
visibility: "private",
Expand All @@ -93,18 +106,21 @@ test.describe("while signed in", () => {
);
expect(response?.status()).toBeLessThan(400);

// await expect(button(page, "Browse Project Code")).toBeVisible();
await expect(button(page, "Edit summary")).toBeVisible();
await expect(navItem(page, "Code")).toBeVisible();
await expect(navItem(page, "Tickets")).toBeVisible();
await expect(navItem(page, "Contributions")).toBeVisible();
await expect(navItem(page, "Settings")).not.toBeVisible();
// Nav is hidden on mobile
if (!isMobile) {
await expect(button(page, "Edit summary")).toBeVisible();
await expect(navItem(page, "Code")).toBeVisible();
await expect(navItem(page, "Tickets")).toBeVisible();
await expect(navItem(page, "Contributions")).toBeVisible();
await expect(navItem(page, "Settings")).not.toBeVisible();
}
});
});

test.describe("with an another user's private project and `project:manage` permission", () => {
test("can view and edit summary, but not see settings", async ({
page,
isMobile,
}) => {
await API.getProject(page, "@bob/private-project", {
visibility: "private",
Expand All @@ -116,12 +132,14 @@ test.describe("while signed in", () => {
);
expect(response?.status()).toBeLessThan(400);

// await expect(button(page, "Browse Project Code")).toBeVisible();
await expect(button(page, "Edit summary")).toBeVisible();
await expect(navItem(page, "Code")).toBeVisible();
await expect(navItem(page, "Tickets")).toBeVisible();
await expect(navItem(page, "Contributions")).toBeVisible();
await expect(navItem(page, "Settings")).toBeVisible();
// Nav is hidden on mobile
if (!isMobile) {
await expect(button(page, "Edit summary")).toBeVisible();
await expect(navItem(page, "Code")).toBeVisible();
await expect(navItem(page, "Tickets")).toBeVisible();
await expect(navItem(page, "Contributions")).toBeVisible();
await expect(navItem(page, "Settings")).toBeVisible();
}
});
});
});
5 changes: 4 additions & 1 deletion tests/e2e/TestHelpers/Data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { faker } from "@faker-js/faker";

// Ensures test data is the same between runs (especially useful for screenshot comparisons)
faker.seed(42);

function account(handle: string) {
return {
...user(),
Expand All @@ -21,7 +24,7 @@ function user(handle?: string) {
const handle_ = handle ? handle : userHandle();

return {
avatarUrl: faker.image.avatar(),
avatarUrl: faker.image.personPortrait(),
handle: handle_.replace("@", ""),
name: `${firstName} ${lastName}`,
userId: faker.string.uuid(),
Expand Down
Loading