mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-23 20:18:42 +08:00
test(mobile): basic e2e tests (#8031)
fix AF-1289 1. tested on 'webkit' 2. a few baseline test cases
This commit is contained in:
@@ -156,7 +156,7 @@ runs:
|
|||||||
- name: Install Playwright's dependencies
|
- name: Install Playwright's dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
if: inputs.playwright-install == 'true'
|
if: inputs.playwright-install == 'true'
|
||||||
run: yarn playwright install --with-deps chromium
|
run: yarn playwright install --with-deps chromium webkit
|
||||||
env:
|
env:
|
||||||
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright
|
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,36 @@ jobs:
|
|||||||
path: ./test-results
|
path: ./test-results
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
e2e-mobile-test:
|
||||||
|
name: E2E Mobile Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DISTRIBUTION: mobile
|
||||||
|
IN_CI_TEST: true
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
shard: [1, 2, 3, 4, 5]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
with:
|
||||||
|
playwright-install: true
|
||||||
|
electron-install: false
|
||||||
|
full-cache: true
|
||||||
|
|
||||||
|
- name: Run playwright tests
|
||||||
|
run: yarn workspace @affine-test/affine-mobile e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }}
|
||||||
|
|
||||||
|
- name: Upload test results
|
||||||
|
if: ${{ failure() }}
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: test-results-e2e-mobile-${{ matrix.shard }}
|
||||||
|
path: ./test-results
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
e2e-migration-test:
|
e2e-migration-test:
|
||||||
name: E2E Migration Test
|
name: E2E Migration Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -28,7 +28,12 @@ export const MobileMenuItem = (props: MenuItemProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onClick={onItemClick} className={className} {...restProps}>
|
<div
|
||||||
|
role="menuitem"
|
||||||
|
onClick={onItemClick}
|
||||||
|
className={className}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import { ResizeObserver } from '@juggle/resize-observer';
|
||||||
|
window.ResizeObserver = ResizeObserver;
|
||||||
|
|
||||||
import '@affine/env/constant';
|
import '@affine/env/constant';
|
||||||
import './edgeless-template';
|
import './edgeless-template';
|
||||||
|
|
||||||
import { setupGlobal } from '@affine/env/global';
|
import { setupGlobal } from '@affine/env/global';
|
||||||
import { ResizeObserver } from '@juggle/resize-observer';
|
|
||||||
|
|
||||||
setupGlobal();
|
setupGlobal();
|
||||||
|
|
||||||
window.ResizeObserver = ResizeObserver;
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export const CategoryDivider = forwardRef(
|
|||||||
<div
|
<div
|
||||||
className={clsx(mobile ? styles.mobileRoot : styles.root, className)}
|
className={clsx(mobile ? styles.mobileRoot : styles.root, className)}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
role="switch"
|
||||||
onClick={() => setCollapsed?.(!collapsed)}
|
onClick={() => setCollapsed?.(!collapsed)}
|
||||||
data-mobile={mobile}
|
data-mobile={mobile}
|
||||||
data-collapsed={collapsed}
|
data-collapsed={collapsed}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export const CollapsibleSection = ({
|
|||||||
{mobile ? null : actions}
|
{mobile ? null : actions}
|
||||||
</CategoryDivider>
|
</CategoryDivider>
|
||||||
<Collapsible.Content
|
<Collapsible.Content
|
||||||
|
data-testid="collapsible-section-content"
|
||||||
className={clsx(mobile ? mobileContent : content, contentClassName)}
|
className={clsx(mobile ? mobileContent : content, contentClassName)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
"browser": "src/index.tsx",
|
"browser": "src/index.tsx",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cross-env DISTRIBUTION=mobile yarn workspace @affine/cli build",
|
"build": "cross-env DISTRIBUTION=mobile yarn workspace @affine/cli build",
|
||||||
"dev": "yarn workspace @affine/cli dev"
|
"dev": "yarn workspace @affine/cli dev",
|
||||||
|
"static-server": "cross-env DISTRIBUTION=mobile yarn workspace @affine/cli dev --static"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@affine/component": "workspace:*",
|
"@affine/component": "workspace:*",
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const AppTabs = () => {
|
|||||||
const location = useLiveData(workbench.location$);
|
const location = useLiveData(workbench.location$);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className={styles.appTabs} id="app-tabs">
|
<ul className={styles.appTabs} id="app-tabs" role="tablist">
|
||||||
{routes.map(route => {
|
{routes.map(route => {
|
||||||
const Link = route.LinkComponent || WorkbenchLink;
|
const Link = route.LinkComponent || WorkbenchLink;
|
||||||
|
|
||||||
@@ -53,6 +53,8 @@ export const AppTabs = () => {
|
|||||||
to={route.to}
|
to={route.to}
|
||||||
key={route.to}
|
key={route.to}
|
||||||
className={styles.tabItem}
|
className={styles.tabItem}
|
||||||
|
role="tab"
|
||||||
|
aria-label={route.to.slice(1)}
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<route.Icon />
|
<route.Icon />
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { IconButton } from '@affine/component';
|
import { IconButton } from '@affine/component';
|
||||||
import { PagePreview } from '@affine/core/components/page-list/page-content-preview';
|
import { PagePreview } from '@affine/core/components/page-list/page-content-preview';
|
||||||
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
||||||
|
import { useCatchEventCallback } from '@affine/core/hooks/use-catch-event-hook';
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
|
||||||
import {
|
import {
|
||||||
WorkbenchLink,
|
WorkbenchLink,
|
||||||
@@ -9,7 +10,7 @@ import {
|
|||||||
import type { DocMeta } from '@blocksuite/store';
|
import type { DocMeta } from '@blocksuite/store';
|
||||||
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { forwardRef, type ReactNode, useCallback } from 'react';
|
import { forwardRef, type ReactNode } from 'react';
|
||||||
|
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
import { DocCardTags } from './tag';
|
import { DocCardTags } from './tag';
|
||||||
@@ -29,8 +30,11 @@ export const DocCard = forwardRef<HTMLAnchorElement, DocCardProps>(
|
|||||||
|
|
||||||
const favorited = useLiveData(favAdapter.isFavorite$(meta.id, 'doc'));
|
const favorited = useLiveData(favAdapter.isFavorite$(meta.id, 'doc'));
|
||||||
|
|
||||||
const toggleFavorite = useCallback(
|
const toggleFavorite = useCatchEventCallback(
|
||||||
() => favAdapter.toggle(meta.id, 'doc'),
|
(e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
favAdapter.toggle(meta.id, 'doc');
|
||||||
|
},
|
||||||
[favAdapter, meta.id]
|
[favAdapter, meta.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -39,13 +43,15 @@ export const DocCard = forwardRef<HTMLAnchorElement, DocCardProps>(
|
|||||||
to={`/${meta.id}`}
|
to={`/${meta.id}`}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={clsx(styles.card, className)}
|
className={clsx(styles.card, className)}
|
||||||
|
data-testid="doc-card"
|
||||||
{...attrs}
|
{...attrs}
|
||||||
>
|
>
|
||||||
<header className={styles.head}>
|
<header className={styles.head} data-testid="doc-card-header">
|
||||||
<h3 className={styles.title}>
|
<h3 className={styles.title}>
|
||||||
{meta.title || <span className={styles.untitled}>Untitled</span>}
|
{meta.title || <span className={styles.untitled}>Untitled</span>}
|
||||||
</h3>
|
</h3>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
aria-label="favorite"
|
||||||
icon={
|
icon={
|
||||||
<IsFavoriteIcon onClick={toggleFavorite} favorite={favorited} />
|
<IsFavoriteIcon onClick={toggleFavorite} favorite={favorited} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,12 @@ export const PageHeader = forwardRef<HTMLHeadElement, PageHeaderProps>(
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header ref={ref} className={clsx(styles.root, className)} {...attrs}>
|
<header
|
||||||
|
data-testid="mobile-page-header"
|
||||||
|
ref={ref}
|
||||||
|
className={clsx(styles.root, className)}
|
||||||
|
{...attrs}
|
||||||
|
>
|
||||||
<section
|
<section
|
||||||
className={clsx(styles.prefix, prefixClassName)}
|
className={clsx(styles.prefix, prefixClassName)}
|
||||||
style={prefixStyle}
|
style={prefixStyle}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export const PageHeaderMenuButton = ({ docId }: PageMenuProps) => {
|
|||||||
<>
|
<>
|
||||||
<MobileMenuItem
|
<MobileMenuItem
|
||||||
prefixIcon={currentMode === 'page' ? <EdgelessIcon /> : <PageIcon />}
|
prefixIcon={currentMode === 'page' ? <EdgelessIcon /> : <PageIcon />}
|
||||||
data-testid="editor-option-menu-edgeless"
|
data-testid="editor-option-menu-mode-switch"
|
||||||
onSelect={handleSwitchMode}
|
onSelect={handleSwitchMode}
|
||||||
>
|
>
|
||||||
{t['Convert to ']()}
|
{t['Convert to ']()}
|
||||||
@@ -133,7 +133,7 @@ export const PageHeaderMenuButton = ({ docId }: PageMenuProps) => {
|
|||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
size={24}
|
size={24}
|
||||||
data-testid="header-dropDownButton"
|
data-testid="detail-page-header-more-button"
|
||||||
className={styles.iconButton}
|
className={styles.iconButton}
|
||||||
>
|
>
|
||||||
<MoreHorizontalIcon />
|
<MoreHorizontalIcon />
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export const Component = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.searchHeader}>
|
<div className={styles.searchHeader} data-testid="search-header">
|
||||||
<SearchInput
|
<SearchInput
|
||||||
debounce={300}
|
debounce={300}
|
||||||
autoFocus={!searchInput}
|
autoFocus={!searchInput}
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ export const RecentDocs = ({ max = 5 }: { max?: number }) => {
|
|||||||
title="Recent"
|
title="Recent"
|
||||||
headerClassName={styles.header}
|
headerClassName={styles.header}
|
||||||
className={styles.recentSection}
|
className={styles.recentSection}
|
||||||
|
testId="recent-docs"
|
||||||
>
|
>
|
||||||
<div className={styles.scroll}>
|
<div className={styles.scroll} data-testid="recent-docs-list">
|
||||||
<ul className={styles.list}>
|
<ul className={styles.list}>
|
||||||
{cardMetas.map((doc, index) => (
|
{cardMetas.map((doc, index) => (
|
||||||
<li key={index} className={styles.cardWrapper}>
|
<li key={index} className={styles.cardWrapper}>
|
||||||
|
|||||||
@@ -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');
|
||||||
|
});
|
||||||
@@ -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();
|
||||||
|
});
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["e2e"],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "../../tests/kit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../../tests/fixtures"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -133,6 +133,9 @@
|
|||||||
{
|
{
|
||||||
"path": "./tests/affine-migration"
|
"path": "./tests/affine-migration"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "./tests/affine-mobile"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "./tests/affine-legacy/0.7.0-canary.18"
|
"path": "./tests/affine-legacy/0.7.0-canary.18"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -127,6 +127,16 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
"@affine-test/affine-mobile@workspace:tests/affine-mobile":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "@affine-test/affine-mobile@workspace:tests/affine-mobile"
|
||||||
|
dependencies:
|
||||||
|
"@affine-test/fixtures": "workspace:*"
|
||||||
|
"@affine-test/kit": "workspace:*"
|
||||||
|
"@playwright/test": "npm:=1.46.1"
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
"@affine-test/fixtures@workspace:*, @affine-test/fixtures@workspace:tests/fixtures":
|
"@affine-test/fixtures@workspace:*, @affine-test/fixtures@workspace:tests/fixtures":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@affine-test/fixtures@workspace:tests/fixtures"
|
resolution: "@affine-test/fixtures@workspace:tests/fixtures"
|
||||||
|
|||||||
Reference in New Issue
Block a user