test(mobile): basic e2e tests (#8031)

fix AF-1289

1. tested on 'webkit'
2. a few baseline test cases
This commit is contained in:
pengx17
2024-09-02 10:20:23 +00:00
parent 41d35fdafd
commit 61e37d8873
22 changed files with 320 additions and 16 deletions

View File

@@ -0,0 +1,34 @@
import { test } from '@affine-test/kit/mobile';
import { expect } from '@playwright/test';
test.beforeEach(async ({ page }) => {
const docsTab = page.locator('#app-tabs').getByRole('tab', { name: 'all' });
await expect(docsTab).toBeVisible();
await docsTab.click();
await page.getByTestId('doc-card').first().click();
await expect(page.locator('.affine-edgeless-viewport')).toBeVisible();
});
test('can open page view more menu', async ({ page }) => {
await page.click('[data-testid="detail-page-header-more-button"]');
await expect(page.getByRole('dialog')).toBeVisible();
});
test('switch to page mode', async ({ page }) => {
await page.click('[data-testid="detail-page-header-more-button"]');
await expect(page.getByRole('dialog')).toBeVisible();
await page.getByRole('menuitem', { name: 'convert to page' }).click();
await expect(page.locator('.doc-title-container')).toBeVisible();
});
test('doc info', async ({ page }) => {
await page.click('[data-testid="detail-page-header-more-button"]');
await expect(page.getByRole('dialog')).toBeVisible();
await page.getByRole('menuitem', { name: 'view info' }).click();
await expect(page.getByRole('button', { name: 'Back' })).toBeVisible();
await expect(page.getByRole('dialog')).toContainText('Created');
await expect(page.getByRole('dialog')).toContainText('Updated');
});

View File

@@ -0,0 +1,63 @@
import { test } from '@affine-test/kit/mobile';
import { expect } from '@playwright/test';
import { expandCollapsibleSection } from './utils';
test('after loaded, will land on the home page', async ({ page }) => {
await expect(page).toHaveURL(/.*\/home/);
});
test('app tabs is visible', async ({ page }) => {
const tabs = page.locator('#app-tabs');
await expect(tabs).toBeVisible();
await expect(tabs.getByRole('tab', { name: 'home' })).toBeVisible();
await expect(tabs.getByRole('tab', { name: 'all' })).toBeVisible();
await expect(tabs.getByRole('tab', { name: 'search' })).toBeVisible();
});
test('recent docs', async ({ page }) => {
const recentSection = await expandCollapsibleSection(page, 'recent');
const docs = recentSection.getByTestId('doc-card');
const firstDoc = docs.first();
await expect(firstDoc).toBeVisible();
const title = await firstDoc
.getByTestId('doc-card-header')
.getByRole('heading')
.textContent();
// when click favorite icon, will show in the favorites section
await docs.getByRole('button', { name: 'favorite' }).first().click();
const favList = await expandCollapsibleSection(page, 'favorites');
await expect(favList).toBeVisible();
if (title) {
await expect(favList).toContainText(title);
}
});
test('all tab', async ({ page }) => {
const docsTab = page.locator('#app-tabs').getByRole('tab', { name: 'all' });
await expect(docsTab).toBeVisible();
await docsTab.click();
const todayDocs = page.getByTestId('doc-card');
expect(await todayDocs.count()).toBeGreaterThan(0);
});
test('search tab', async ({ page }) => {
const searchTab = page
.locator('#app-tabs')
.getByRole('tab', { name: 'search' });
await expect(searchTab).toBeVisible();
await searchTab.click();
const searchInput = page.getByTestId('search-header').getByRole('textbox');
await expect(searchInput).toBeVisible();
});

View File

@@ -0,0 +1,15 @@
/* eslint-disable unicorn/prefer-dom-node-dataset */
import { expect, type Page } from '@playwright/test';
export async function expandCollapsibleSection(page: Page, name: string) {
const divider = page.locator(`[data-collapsible]:has-text("${name}")`);
if ((await divider.getAttribute('data-collapsed')) === 'true') {
await divider.click();
}
await expect(divider).toHaveAttribute('data-collapsed', 'false');
const section = divider.locator(
'~ [data-testid="collapsible-section-content"]'
);
await expect(section).toBeVisible();
return section;
}

View File

@@ -0,0 +1,13 @@
{
"name": "@affine-test/affine-mobile",
"private": true,
"scripts": {
"e2e": "yarn playwright test"
},
"devDependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine-test/kit": "workspace:*",
"@playwright/test": "=1.46.1"
},
"version": "0.16.0"
}

View File

@@ -0,0 +1,74 @@
import { testResultDir } from '@affine-test/kit/playwright';
import { devices, type PlaywrightTestConfig } from '@playwright/test';
/**
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
testDir: './e2e',
fullyParallel: true,
timeout: process.env.CI ? 60_000 : 30_000,
outputDir: testResultDir,
projects: [
{
name: 'Mobile Safari',
use: {
...devices['iPhone 14'],
},
},
{
name: 'Mobile Chrome',
use: {
...devices['Pixel 5'],
},
},
],
use: {
baseURL: 'http://localhost:8080/',
actionTimeout: 10 * 1000,
locale: 'en-US',
// Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer
// You can open traces locally(`npx playwright show-trace trace.zip`)
// or in your browser on [Playwright Trace Viewer](https://trace.playwright.dev/).
trace: 'on-first-retry',
// Record video only when retrying a test for the first time.
video: 'on-first-retry',
},
forbidOnly: !!process.env.CI,
workers: 4,
retries: 1,
// 'github' for GitHub Actions CI to generate annotations, plus a concise 'dot'
// default 'list' when running locally
// See https://playwright.dev/docs/test-reporters#github-actions-annotations
reporter: process.env.CI ? 'github' : 'list',
webServer: [
{
command: 'yarn run serve:test-static',
port: 8081,
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
env: {
COVERAGE: process.env.COVERAGE || 'false',
ENABLE_DEBUG_PAGE: '1',
},
},
// Intentionally not building the web, reminds you to run it by yourself.
{
command: 'yarn workspace @affine/mobile static-server',
port: 8080,
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
env: {
COVERAGE: process.env.COVERAGE || 'false',
},
},
],
};
if (process.env.CI) {
config.retries = 3;
config.workers = '50%';
}
export default config;

View File

@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"esModuleInterop": true,
"outDir": "lib"
},
"include": ["e2e"],
"references": [
{
"path": "../../tests/kit"
},
{
"path": "../../tests/fixtures"
}
]
}

24
tests/kit/mobile.ts Normal file
View File

@@ -0,0 +1,24 @@
import { expect } from '@playwright/test';
import { test as baseTest } from './playwright';
type CurrentDocCollection = {
meta: { id: string; flavour: string };
};
export const test = baseTest.extend<{
workspace: {
current: () => Promise<CurrentDocCollection>;
};
}>({
page: async ({ page }, use) => {
await page.goto('/');
await expect(
page.locator('.affine-page-viewport[data-mode="edgeless"]')
).toBeVisible({
timeout: 30 * 1000,
});
await page.goto('/');
await use(page);
},
});