fix(workspace): check affine login auth (#2070)

This commit is contained in:
Himself65
2023-04-21 20:44:29 -05:00
committed by GitHub
parent 2c95a0a757
commit 1ca9fb8ff4
19 changed files with 211 additions and 73 deletions

View File

@@ -25,14 +25,19 @@ export const createAffineDownloadProvider = (
); );
return; return;
} }
affineApis.downloadWorkspace(id, false).then(binary => { affineApis
hashMap.set(id, binary); .downloadWorkspace(id, false)
providerLogger.debug('applyUpdate'); .then(binary => {
BlockSuiteWorkspace.Y.applyUpdate( hashMap.set(id, binary);
blockSuiteWorkspace.doc, providerLogger.debug('applyUpdate');
new Uint8Array(binary) BlockSuiteWorkspace.Y.applyUpdate(
); blockSuiteWorkspace.doc,
}); new Uint8Array(binary)
);
})
.catch(e => {
providerLogger.error('downloadWorkspace', e);
});
}, },
disconnect: () => { disconnect: () => {
providerLogger.info('disconnect download provider', id); providerLogger.info('disconnect download provider', id);

View File

@@ -15,29 +15,37 @@ const logger = new DebugLogger('auth-token');
const revalidate = async () => { const revalidate = async () => {
const storage = getLoginStorage(); const storage = getLoginStorage();
if (storage) { if (storage) {
const tokenMessage = parseIdToken(storage.token); try {
logger.debug('revalidate affine user'); const tokenMessage = parseIdToken(storage.token);
if (isExpired(tokenMessage)) { logger.debug('revalidate affine user');
logger.debug('need to refresh token'); if (isExpired(tokenMessage)) {
const response = await affineAuth.refreshToken(storage); logger.debug('need to refresh token');
if (response) { const response = await affineAuth.refreshToken(storage);
setLoginStorage(response); if (response) {
storageChangeSlot.emit(); setLoginStorage(response);
storageChangeSlot.emit();
}
} }
} catch (e) {
return false;
} }
return true;
} else {
return false;
} }
return true;
}; };
export function useAffineRefreshAuthToken( export function useAffineRefreshAuthToken(
// every 30 seconds, check if the token is expired // every 30 seconds, check if the token is expired
refreshInterval = 30 * 1000 refreshInterval = 30 * 1000
) { ) {
useSWR('autoRefreshToken', { const { data } = useSWR<boolean>('autoRefreshToken', {
suspense: true,
fetcher: revalidate, fetcher: revalidate,
refreshInterval, refreshInterval,
revalidateOnFocus: true, revalidateOnFocus: true,
revalidateOnReconnect: true, revalidateOnReconnect: true,
revalidateOnMount: true, revalidateOnMount: true,
}); });
return data;
} }

View File

@@ -20,6 +20,19 @@ export function useSyncRouterWithCurrentWorkspaceId(router: NextRouter) {
return; return;
} }
if (currentWorkspaceId) { if (currentWorkspaceId) {
if (currentWorkspaceId !== workspaceId) {
const target = metadata.find(workspace => workspace.id === workspaceId);
if (!target) {
// workspaceId is invalid, redirect to currentWorkspaceId
void router.push({
pathname: router.pathname,
query: {
...router.query,
workspaceId: currentWorkspaceId,
},
});
}
}
return; return;
} }
const targetWorkspace = metadata.find( const targetWorkspace = metadata.find(

View File

@@ -17,7 +17,14 @@ import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import Head from 'next/head'; import Head from 'next/head';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import type { FC, PropsWithChildren, ReactElement } from 'react'; import type { FC, PropsWithChildren, ReactElement } from 'react';
import { lazy, Suspense, useCallback, useEffect, useState } from 'react'; import {
lazy,
Suspense,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { openQuickSearchModalAtom, openWorkspacesModalAtom } from '../atoms'; import { openQuickSearchModalAtom, openWorkspacesModalAtom } from '../atoms';
import { import {
@@ -176,6 +183,10 @@ export const WorkspaceLayout: FC<PropsWithChildren> =
}, [i18n]); }, [i18n]);
const currentWorkspaceId = useAtomValue(rootCurrentWorkspaceIdAtom); const currentWorkspaceId = useAtomValue(rootCurrentWorkspaceIdAtom);
const jotaiWorkspaces = useAtomValue(rootWorkspacesMetadataAtom); const jotaiWorkspaces = useAtomValue(rootWorkspacesMetadataAtom);
const meta = useMemo(
() => jotaiWorkspaces.find(x => x.id === currentWorkspaceId),
[currentWorkspaceId, jotaiWorkspaces]
);
const set = useSetAtom(rootWorkspacesMetadataAtom); const set = useSetAtom(rootWorkspacesMetadataAtom);
useEffect(() => { useEffect(() => {
logger.info('mount'); logger.info('mount');
@@ -228,6 +239,9 @@ export const WorkspaceLayout: FC<PropsWithChildren> =
}; };
} }
}, [currentWorkspaceId, jotaiWorkspaces]); }, [currentWorkspaceId, jotaiWorkspaces]);
const Provider =
(meta && WorkspacePlugins[meta.flavour].UI.Provider) ?? DefaultProvider;
return ( return (
<> <>
{/* fixme(himself65): don't re-render whole modals */} {/* fixme(himself65): don't re-render whole modals */}
@@ -240,7 +254,9 @@ export const WorkspaceLayout: FC<PropsWithChildren> =
<Suspense <Suspense
fallback={<PageLoading text={t('Finding Current Workspace')} />} fallback={<PageLoading text={t('Finding Current Workspace')} />}
> >
<WorkspaceLayoutInner>{children}</WorkspaceLayoutInner> <Provider>
<WorkspaceLayoutInner>{children}</WorkspaceLayoutInner>
</Provider>
</Suspense> </Suspense>
</CurrentWorkspaceContext> </CurrentWorkspaceContext>
</> </>
@@ -397,11 +413,8 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
); );
}, [setIsResizing, setSidebarOpen, setSliderWidth]); }, [setIsResizing, setSidebarOpen, setSliderWidth]);
const Provider =
WorkspacePlugins[currentWorkspace.flavour].UI.Provider ?? DefaultProvider;
return ( return (
<Provider> <>
<Head> <Head>
<title>{title}</title> <title>{title}</title>
</Head> </Head>
@@ -465,6 +478,6 @@ export const WorkspaceLayoutInner: FC<PropsWithChildren> = ({ children }) => {
</MainContainerWrapper> </MainContainerWrapper>
</StyledPage> </StyledPage>
<QuickSearch /> <QuickSearch />
</Provider> </>
); );
}; };

View File

@@ -1,6 +1,5 @@
import { DebugLogger } from '@affine/debug'; import { DebugLogger } from '@affine/debug';
import { useTranslation } from '@affine/i18n'; import { useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { Suspense, useEffect } from 'react'; import { Suspense, useEffect } from 'react';
@@ -27,10 +26,7 @@ const IndexPageInner = () => {
const targetWorkspace = const targetWorkspace =
(lastWorkspaceId && (lastWorkspaceId &&
workspaces.find(({ id }) => id === lastWorkspaceId)) || workspaces.find(({ id }) => id === lastWorkspaceId)) ||
// fixme(himself65): workspaces.at(0);
// when affine workspace is expired and the first workspace is affine,
// the page will crash
workspaces.find(({ flavour }) => flavour === WorkspaceFlavour.LOCAL);
if (targetWorkspace) { if (targetWorkspace) {
const pageId = const pageId =

View File

@@ -1,10 +1,11 @@
import { config, prefixUrl } from '@affine/env'; import { AFFINE_STORAGE_KEY, config, prefixUrl } from '@affine/env';
import { initPage } from '@affine/env/blocksuite'; import { initPage } from '@affine/env/blocksuite';
import { currentAffineUserAtom } from '@affine/workspace/affine/atom'; import { currentAffineUserAtom } from '@affine/workspace/affine/atom';
import { import {
clearLoginStorage, clearLoginStorage,
createAffineAuth, createAffineAuth,
getLoginStorage, getLoginStorage,
isExpired,
parseIdToken, parseIdToken,
setLoginStorage, setLoginStorage,
SignMethod, SignMethod,
@@ -12,9 +13,13 @@ import {
import { rootStore, rootWorkspacesMetadataAtom } from '@affine/workspace/atom'; import { rootStore, rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import type { AffineWorkspace } from '@affine/workspace/type'; import type { AffineWorkspace } from '@affine/workspace/type';
import { LoadPriority, WorkspaceFlavour } from '@affine/workspace/type'; import { LoadPriority, WorkspaceFlavour } from '@affine/workspace/type';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import {
cleanupWorkspace,
createEmptyBlockSuiteWorkspace,
} from '@affine/workspace/utils';
import { createJSONStorage } from 'jotai/utils'; import { createJSONStorage } from 'jotai/utils';
import React from 'react'; import type { PropsWithChildren, ReactElement } from 'react';
import { Suspense, useEffect } from 'react';
import { mutate } from 'swr'; import { mutate } from 'swr';
import { z } from 'zod'; import { z } from 'zod';
@@ -23,8 +28,8 @@ import { PageNotFoundError } from '../../components/affine/affine-error-eoundary
import { WorkspaceSettingDetail } from '../../components/affine/workspace-setting-detail'; import { WorkspaceSettingDetail } from '../../components/affine/workspace-setting-detail';
import { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list'; import { BlockSuitePageList } from '../../components/blocksuite/block-suite-page-list';
import { PageDetailEditor } from '../../components/page-detail-editor'; import { PageDetailEditor } from '../../components/page-detail-editor';
import { PageLoading } from '../../components/pure/loading';
import { useAffineRefreshAuthToken } from '../../hooks/affine/use-affine-refresh-auth-token'; import { useAffineRefreshAuthToken } from '../../hooks/affine/use-affine-refresh-auth-token';
import { AffineSWRConfigProvider } from '../../providers/AffineSWRConfigProvider';
import { BlockSuiteWorkspace } from '../../shared'; import { BlockSuiteWorkspace } from '../../shared';
import { affineApis } from '../../shared/apis'; import { affineApis } from '../../shared/apis';
import { toast } from '../../utils'; import { toast } from '../../utils';
@@ -32,7 +37,6 @@ import type { WorkspacePlugin } from '..';
import { QueryKey } from './fetcher'; import { QueryKey } from './fetcher';
const storage = createJSONStorage(() => localStorage); const storage = createJSONStorage(() => localStorage);
const kAffineLocal = 'affine-local-storage-v2';
const schema = z.object({ const schema = z.object({
id: z.string(), id: z.string(),
type: z.number(), type: z.number(),
@@ -41,7 +45,7 @@ const schema = z.object({
}); });
const getPersistenceAllWorkspace = () => { const getPersistenceAllWorkspace = () => {
const items = storage.getItem(kAffineLocal); const items = storage.getItem(AFFINE_STORAGE_KEY);
const allWorkspaces: AffineWorkspace[] = []; const allWorkspaces: AffineWorkspace[] = [];
if ( if (
Array.isArray(items) && Array.isArray(items) &&
@@ -71,6 +75,22 @@ const getPersistenceAllWorkspace = () => {
export const affineAuth = createAffineAuth(prefixUrl); export const affineAuth = createAffineAuth(prefixUrl);
function AuthContext({ children }: PropsWithChildren): ReactElement {
const login = useAffineRefreshAuthToken();
useEffect(() => {
if (!login) {
console.warn('No login, redirecting to local workspace page...');
}
}, [login]);
if (!login) {
return (
<PageLoading text="No login, redirecting to local workspace page..." />
);
}
return <>{children}</>;
}
export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = { export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
flavour: WorkspaceFlavour.AFFINE, flavour: WorkspaceFlavour.AFFINE,
loadPriority: LoadPriority.HIGH, loadPriority: LoadPriority.HIGH,
@@ -99,7 +119,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
workspace => workspace.flavour !== WorkspaceFlavour.AFFINE workspace => workspace.flavour !== WorkspaceFlavour.AFFINE
) )
); );
storage.removeItem(kAffineLocal); storage.removeItem(AFFINE_STORAGE_KEY);
clearLoginStorage(); clearLoginStorage();
rootStore.set(currentAffineUserAtom, null); rootStore.set(currentAffineUserAtom, null);
}, },
@@ -132,13 +152,13 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
return id; return id;
}, },
delete: async workspace => { delete: async workspace => {
const items = storage.getItem(kAffineLocal); const items = storage.getItem(AFFINE_STORAGE_KEY);
if ( if (
Array.isArray(items) && Array.isArray(items) &&
items.every(item => schema.safeParse(item).success) items.every(item => schema.safeParse(item).success)
) { ) {
storage.setItem( storage.setItem(
kAffineLocal, AFFINE_STORAGE_KEY,
items.filter(item => item.id !== workspace.id) items.filter(item => item.id !== workspace.id)
); );
} }
@@ -148,12 +168,17 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
await mutate(matcher => matcher === QueryKey.getWorkspaces); await mutate(matcher => matcher === QueryKey.getWorkspaces);
}, },
get: async workspaceId => { get: async workspaceId => {
// fixme(himself65): rewrite the auth logic
try { try {
if (!getLoginStorage()) { const loginStorage = getLoginStorage();
const workspaces = getPersistenceAllWorkspace(); if (
return ( loginStorage == null ||
workspaces.find(workspace => workspace.id === workspaceId) ?? null isExpired(parseIdToken(loginStorage.token))
); ) {
rootStore.set(currentAffineUserAtom, null);
storage.removeItem(AFFINE_STORAGE_KEY);
cleanupWorkspace(WorkspaceFlavour.AFFINE);
return null;
} }
const workspaces: AffineWorkspace[] = await AffinePlugin.CRUD.list(); const workspaces: AffineWorkspace[] = await AffinePlugin.CRUD.list();
return ( return (
@@ -168,8 +193,20 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
}, },
list: async () => { list: async () => {
const allWorkspaces = getPersistenceAllWorkspace(); const allWorkspaces = getPersistenceAllWorkspace();
if (!getLoginStorage()) { const loginStorage = getLoginStorage();
return allWorkspaces; // fixme(himself65): rewrite the auth logic
try {
if (
loginStorage == null ||
isExpired(parseIdToken(loginStorage.token))
) {
rootStore.set(currentAffineUserAtom, null);
storage.removeItem(AFFINE_STORAGE_KEY);
return [];
}
} catch (e) {
storage.removeItem(AFFINE_STORAGE_KEY);
return [];
} }
try { try {
const workspaces = await affineApis.getWorkspaces().then(workspaces => { const workspaces = await affineApis.getWorkspaces().then(workspaces => {
@@ -189,7 +226,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
permission: workspace.permission, permission: workspace.permission,
} satisfies z.infer<typeof schema>; } satisfies z.infer<typeof schema>;
}); });
const old = storage.getItem(kAffineLocal); const old = storage.getItem(AFFINE_STORAGE_KEY);
if ( if (
Array.isArray(old) && Array.isArray(old) &&
old.every(item => schema.safeParse(item).success) old.every(item => schema.safeParse(item).success)
@@ -201,7 +238,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
data.push(item); data.push(item);
} }
}); });
storage.setItem(kAffineLocal, [...data]); storage.setItem(AFFINE_STORAGE_KEY, [...data]);
} }
const affineWorkspace: AffineWorkspace = { const affineWorkspace: AffineWorkspace = {
@@ -231,7 +268,7 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
permission: workspace.permission, permission: workspace.permission,
} satisfies z.infer<typeof schema>; } satisfies z.infer<typeof schema>;
}); });
storage.setItem(kAffineLocal, [...dump]); storage.setItem(AFFINE_STORAGE_KEY, [...dump]);
} catch (e) { } catch (e) {
console.error('fetch affine workspaces failed', e); console.error('fetch affine workspaces failed', e);
} }
@@ -240,8 +277,11 @@ export const AffinePlugin: WorkspacePlugin<WorkspaceFlavour.AFFINE> = {
}, },
UI: { UI: {
Provider: ({ children }) => { Provider: ({ children }) => {
useAffineRefreshAuthToken(); return (
return <AffineSWRConfigProvider>{children}</AffineSWRConfigProvider>; <Suspense fallback={<PageLoading text="Checking login status..." />}>
<AuthContext>{children}</AuthContext>
</Suspense>
);
}, },
PageDetail: ({ currentWorkspace, currentPageId }) => { PageDetail: ({ currentWorkspace, currentPageId }) => {
const page = currentWorkspace.blockSuiteWorkspace.getPage(currentPageId); const page = currentWorkspace.blockSuiteWorkspace.getPage(currentPageId);

View File

@@ -1,3 +1,4 @@
export const AFFINE_STORAGE_KEY = 'affine-local-storage-v2';
export const DEFAULT_WORKSPACE_NAME = 'Demo Workspace'; export const DEFAULT_WORKSPACE_NAME = 'Demo Workspace';
export const UNTITLED_WORKSPACE_NAME = 'Untitled'; export const UNTITLED_WORKSPACE_NAME = 'Untitled';
export const DEFAULT_HELLO_WORLD_PAGE_ID = 'hello-world'; export const DEFAULT_HELLO_WORLD_PAGE_ID = 'hello-world';

View File

@@ -403,7 +403,7 @@ export function createWorkspaceApis(prefixUrl = '/') {
}, },
}) })
.then(r => .then(r =>
r.status === 403 !r.ok
? Promise.reject(new RequestError(MessageCode.noPermission)) ? Promise.reject(new RequestError(MessageCode.noPermission))
: r : r
) )

View File

@@ -4,6 +4,8 @@ import {
isExpired, isExpired,
parseIdToken, parseIdToken,
} from '@affine/workspace/affine/login'; } from '@affine/workspace/affine/login';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { cleanupWorkspace } from '@affine/workspace/utils';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
import * as url from 'lib0/url'; import * as url from 'lib0/url';
import * as websocket from 'lib0/websocket'; import * as websocket from 'lib0/websocket';
@@ -28,6 +30,10 @@ export class WebsocketClient {
public connect(callback: (message: any) => void) { public connect(callback: (message: any) => void) {
const loginResponse = getLoginStorage(); const loginResponse = getLoginStorage();
if (!loginResponse || isExpired(parseIdToken(loginResponse.token))) {
cleanupWorkspace(WorkspaceFlavour.AFFINE);
return;
}
assertExists(loginResponse, 'loginResponse is null'); assertExists(loginResponse, 'loginResponse is null');
const encodedParams = url.encodeQueryParams({ const encodedParams = url.encodeQueryParams({
token: loginResponse.token, token: loginResponse.token,

View File

@@ -1,4 +1,5 @@
import type { createWorkspaceApis } from '@affine/workspace/affine/api'; import type { createWorkspaceApis } from '@affine/workspace/affine/api';
import { rootStore, rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import { createAffineBlobStorage } from '@affine/workspace/blob'; import { createAffineBlobStorage } from '@affine/workspace/blob';
import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models';
import type { Generator, StoreOptions } from '@blocksuite/store'; import type { Generator, StoreOptions } from '@blocksuite/store';
@@ -7,6 +8,12 @@ import { createIndexeddbStorage, Workspace } from '@blocksuite/store';
import { createSQLiteStorage } from './blob/sqlite-blob-storage'; import { createSQLiteStorage } from './blob/sqlite-blob-storage';
import { WorkspaceFlavour } from './type'; import { WorkspaceFlavour } from './type';
export function cleanupWorkspace(flavour: WorkspaceFlavour) {
rootStore.set(rootWorkspacesMetadataAtom, metas =>
metas.filter(meta => meta.flavour !== flavour)
);
}
const hashMap = new Map<string, Workspace>(); const hashMap = new Map<string, Workspace>();
export function createEmptyBlockSuiteWorkspace( export function createEmptyBlockSuiteWorkspace(

View File

@@ -2,8 +2,10 @@ import type { Page } from '@playwright/test';
import { getMetas } from './utils'; import { getMetas } from './utils';
export const webUrl = 'http://localhost:8080';
export async function openHomePage(page: Page) { export async function openHomePage(page: Page) {
await page.goto('http://localhost:8080'); await page.goto(webUrl);
} }
export async function initHomePageWithPinboard(page: Page) { export async function initHomePageWithPinboard(page: Page) {

View File

@@ -131,10 +131,6 @@ export async function loginUser(
}, token); }, token);
} }
export async function openHomePage(page: Page) {
return page.goto('http://localhost:8080');
}
export async function getMetas(page: Page): Promise<PageMeta[]> { export async function getMetas(page: Page): Promise<PageMeta[]> {
return page.evaluate( return page.evaluate(
() => globalThis.currentWorkspace.blockSuiteWorkspace.meta.pageMetas ?? [] () => globalThis.currentWorkspace.blockSuiteWorkspace.meta.pageMetas ?? []

View File

@@ -1,6 +1,9 @@
import type { Page } from '@playwright/test'; import type { Page } from '@playwright/test';
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { clickCollaborationPanel } from './setting';
import { clickSideBarSettingButton } from './sidebar';
interface CreateWorkspaceParams { interface CreateWorkspaceParams {
name: string; name: string;
} }
@@ -34,3 +37,14 @@ export async function assertCurrentWorkspaceFlavour(
const actual = await page.evaluate(() => globalThis.currentWorkspace.flavour); const actual = await page.evaluate(() => globalThis.currentWorkspace.flavour);
expect(actual).toBe(flavour); expect(actual).toBe(flavour);
} }
export async function enableAffineCloudWorkspace(page: Page) {
await clickSideBarSettingButton(page);
await page.waitForTimeout(50);
await clickCollaborationPanel(page);
await page.getByTestId('local-workspace-enable-cloud-button').click();
await page.getByTestId('confirm-enable-cloud-button').click();
await page.waitForSelector("[data-testid='member-length']", {
timeout: 20000,
});
}

View File

@@ -1,12 +1,13 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { openHomePage } from '../../libs/load-page';
import { waitMarkdownImported } from '../../libs/page-logic'; import { waitMarkdownImported } from '../../libs/page-logic';
import { test } from '../../libs/playwright'; import { test } from '../../libs/playwright';
import { import {
clickNewPageButton, clickNewPageButton,
clickSideBarCurrentWorkspaceBanner, clickSideBarCurrentWorkspaceBanner,
} from '../../libs/sidebar'; } from '../../libs/sidebar';
import { getBuiltInUser, loginUser, openHomePage } from '../../libs/utils'; import { getBuiltInUser, loginUser } from '../../libs/utils';
test('collaborative', async ({ page, browser }) => { test('collaborative', async ({ page, browser }) => {
await openHomePage(page); await openHomePage(page);

View File

@@ -0,0 +1,18 @@
import { openHomePage } from '../../libs/load-page';
import { waitMarkdownImported } from '../../libs/page-logic';
import { test } from '../../libs/playwright';
import { clickSideBarAllPageButton } from '../../libs/sidebar';
import { createFakeUser, loginUser } from '../../libs/utils';
import { enableAffineCloudWorkspace } from '../../libs/workspace';
test('authorization expired', async ({ page }) => {
await openHomePage(page);
await waitMarkdownImported(page);
const [a] = await createFakeUser();
await loginUser(page, a);
await enableAffineCloudWorkspace(page);
await clickSideBarAllPageButton(page);
await page.evaluate(() => localStorage.removeItem('affine-login-v2'));
await openHomePage(page);
await waitMarkdownImported(page);
});

View File

@@ -1,9 +1,10 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { openHomePage } from '../../libs/load-page';
import { waitMarkdownImported } from '../../libs/page-logic'; import { waitMarkdownImported } from '../../libs/page-logic';
import { test } from '../../libs/playwright'; import { test } from '../../libs/playwright';
import { clickNewPageButton } from '../../libs/sidebar'; import { clickNewPageButton } from '../../libs/sidebar';
import { createFakeUser, loginUser, openHomePage } from '../../libs/utils'; import { createFakeUser, loginUser } from '../../libs/utils';
import { createWorkspace } from '../../libs/workspace'; import { createWorkspace } from '../../libs/workspace';
test('public single page', async ({ page, browser }) => { test('public single page', async ({ page, browser }) => {

View File

@@ -1,5 +1,6 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { openHomePage } from '../../libs/load-page';
import { waitMarkdownImported } from '../../libs/page-logic'; import { waitMarkdownImported } from '../../libs/page-logic';
import { test } from '../../libs/playwright'; import { test } from '../../libs/playwright';
import { clickPublishPanel } from '../../libs/setting'; import { clickPublishPanel } from '../../libs/setting';
@@ -7,7 +8,7 @@ import {
clickSideBarAllPageButton, clickSideBarAllPageButton,
clickSideBarSettingButton, clickSideBarSettingButton,
} from '../../libs/sidebar'; } from '../../libs/sidebar';
import { createFakeUser, loginUser, openHomePage } from '../../libs/utils'; import { createFakeUser, loginUser } from '../../libs/utils';
import { createWorkspace } from '../../libs/workspace'; import { createWorkspace } from '../../libs/workspace';
test('enable public workspace', async ({ page, context }) => { test('enable public workspace', async ({ page, context }) => {

View File

@@ -1,5 +1,6 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { openHomePage } from '../../libs/load-page';
import { waitMarkdownImported } from '../../libs/page-logic'; import { waitMarkdownImported } from '../../libs/page-logic';
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -7,17 +8,15 @@ const userA = require('../../fixtures/userA.json');
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const userB = require('../../fixtures/userB.json'); const userB = require('../../fixtures/userB.json');
import { test } from '../../libs/playwright'; import { test } from '../../libs/playwright';
import { clickCollaborationPanel } from '../../libs/setting';
import { import {
clickNewPageButton, clickNewPageButton,
clickSideBarAllPageButton,
clickSideBarCurrentWorkspaceBanner, clickSideBarCurrentWorkspaceBanner,
clickSideBarSettingButton,
} from '../../libs/sidebar'; } from '../../libs/sidebar';
import { createFakeUser, loginUser, openHomePage } from '../../libs/utils'; import { createFakeUser, loginUser } from '../../libs/utils';
import { import {
assertCurrentWorkspaceFlavour, assertCurrentWorkspaceFlavour,
createWorkspace, createWorkspace,
enableAffineCloudWorkspace,
openWorkspaceListModal, openWorkspaceListModal,
} from '../../libs/workspace'; } from '../../libs/workspace';
@@ -41,15 +40,7 @@ test('should enable affine workspace successfully', async ({ page }) => {
const name = `test-${Date.now()}`; const name = `test-${Date.now()}`;
await createWorkspace({ name }, page); await createWorkspace({ name }, page);
await page.waitForTimeout(50); await page.waitForTimeout(50);
await clickSideBarSettingButton(page); await enableAffineCloudWorkspace(page);
await page.waitForTimeout(50);
await clickCollaborationPanel(page);
await page.getByTestId('local-workspace-enable-cloud-button').click();
await page.getByTestId('confirm-enable-cloud-button').click();
await page.waitForSelector("[data-testid='member-length']", {
timeout: 20000,
});
await clickSideBarAllPageButton(page);
await clickNewPageButton(page); await clickNewPageButton(page);
await page.locator('[data-block-is-title="true"]').type('Hello, world!', { await page.locator('[data-block-is-title="true"]').type('Hello, world!', {
delay: 50, delay: 50,

View File

@@ -0,0 +1,25 @@
import { expect } from '@playwright/test';
import { openHomePage, webUrl } from '../libs/load-page';
import { waitMarkdownImported } from '../libs/page-logic';
import { test } from '../libs/playwright';
import { clickSideBarAllPageButton } from '../libs/sidebar';
test('goto not found page', async ({ page }) => {
await openHomePage(page);
await waitMarkdownImported(page);
const currentUrl = page.url();
const invalidUrl = currentUrl.replace(/\/$/, '') + '/invalid';
await page.goto(invalidUrl);
expect(await page.getByTestId('notFound').isVisible()).toBeTruthy();
});
test('goto not found workspace', async ({ page }) => {
await openHomePage(page);
await waitMarkdownImported(page);
await clickSideBarAllPageButton(page);
const currentUrl = page.url();
await page.goto(new URL('/workspace/invalid/all', webUrl).toString());
await clickSideBarAllPageButton(page);
expect(page.url()).toEqual(currentUrl);
});