test(core): enable no-floating-promises rule for tests (#11915)

Sometimes, missing `await` in the test code can cause timing issues, leading to test failures. This PR enables the `no-floating-promises` rule for the test code to ensure that such errors do not occur.
This commit is contained in:
L-Sun 2025-04-23 08:17:41 +00:00
parent 200015a811
commit a9ad01491c
No known key found for this signature in database
GPG Key ID: D5C252102D2B576F
28 changed files with 136 additions and 121 deletions

View File

@ -11,7 +11,7 @@ import { addSampleNotes } from './doc-generator.js';
import { createPainterWorker, setupEditor } from './setup.js'; import { createPainterWorker, setupEditor } from './setup.js';
async function init() { async function init() {
setupEditor('edgeless', [ await setupEditor('edgeless', [
ParagraphLayoutHandlerExtension, ParagraphLayoutHandlerExtension,
ListLayoutHandlerExtension, ListLayoutHandlerExtension,
ImageLayoutHandlerExtension, ImageLayoutHandlerExtension,
@ -29,4 +29,4 @@ async function init() {
window.renderer = renderer; window.renderer = renderer;
} }
init(); await init();

View File

@ -299,7 +299,10 @@ export default tseslint.config(
'**/e2e/**/*', '**/e2e/**/*',
], ],
rules: { rules: {
'@typescript-eslint/no-floating-promises': 0, '@typescript-eslint/no-floating-promises': [
'error',
{ ignoreVoid: true },
],
'@typescript-eslint/no-misused-promises': 0, '@typescript-eslint/no-misused-promises': 0,
'@typescript-eslint/no-restricted-imports': 0, '@typescript-eslint/no-restricted-imports': 0,
}, },

View File

@ -181,7 +181,7 @@ test('should create session correctly', async t => {
); );
}); });
app.switchUser(u1); await app.switchUser(u1);
const inviteId = await inviteUser(app, id, u2.email); const inviteId = await inviteUser(app, id, u2.email);
await app.login(u2); await app.login(u2);
await acceptInviteById(app, id, inviteId, false); await acceptInviteById(app, id, inviteId, false);
@ -329,9 +329,9 @@ test('should fork session correctly', async t => {
); );
}); });
app.switchUser(u1); await app.switchUser(u1);
const inviteId = await inviteUser(app, id, u2.email); const inviteId = await inviteUser(app, id, u2.email);
app.switchUser(u2); await app.switchUser(u2);
await acceptInviteById(app, id, inviteId, false); await acceptInviteById(app, id, inviteId, false);
await assertForkSession(id, sessionId, randomUUID(), '', async x => { await assertForkSession(id, sessionId, randomUUID(), '', async x => {
await t.throwsAsync( await t.throwsAsync(
@ -341,14 +341,14 @@ test('should fork session correctly', async t => {
); );
}); });
app.switchUser(u1); await app.switchUser(u1);
const histories = await getHistories(app, { workspaceId: id }); const histories = await getHistories(app, { workspaceId: id });
const latestMessageId = histories const latestMessageId = histories
.find(h => h.sessionId === forkedSessionId) .find(h => h.sessionId === forkedSessionId)
?.messages.findLast(m => m.role === 'assistant')?.id; ?.messages.findLast(m => m.role === 'assistant')?.id;
t.truthy(latestMessageId, 'should find latest message id'); t.truthy(latestMessageId, 'should find latest message id');
app.switchUser(u2); await app.switchUser(u2);
await assertForkSession( await assertForkSession(
id, id,
forkedSessionId, forkedSessionId,
@ -654,10 +654,10 @@ test('should reject request from different user', async t => {
// should reject chat from different user // should reject chat from different user
{ {
app.switchUser(u1); await app.switchUser(u1);
const messageId = await createCopilotMessage(app, sessionId); const messageId = await createCopilotMessage(app, sessionId);
{ {
app.switchUser(u2); await app.switchUser(u2);
await t.throwsAsync( await t.throwsAsync(
chatWithText(app, sessionId, messageId), chatWithText(app, sessionId, messageId),
{ instanceOf: Error }, { instanceOf: Error },
@ -723,9 +723,9 @@ test('should reject request that user have not permission', async t => {
// should able to list history after user have permission // should able to list history after user have permission
{ {
app.switchUser(u1); await app.switchUser(u1);
const inviteId = await inviteUser(app, workspaceId, u2.email); const inviteId = await inviteUser(app, workspaceId, u2.email);
app.switchUser(u2); await app.switchUser(u2);
await acceptInviteById(app, workspaceId, inviteId, false); await acceptInviteById(app, workspaceId, inviteId, false);
t.deepEqual( t.deepEqual(
@ -753,7 +753,7 @@ test('should reject request that user have not permission', async t => {
'should able to list history' 'should able to list history'
); );
app.switchUser(u1); await app.switchUser(u1);
t.deepEqual( t.deepEqual(
await getHistories(app, { workspaceId }), await getHistories(app, { workspaceId }),
[], [],

View File

@ -6,6 +6,6 @@ export const e2e = test;
// @ts-expect-error created in prelude.ts // @ts-expect-error created in prelude.ts
export const app: TestingApp = globalThis.app; export const app: TestingApp = globalThis.app;
registerCompletionHandler(() => { registerCompletionHandler(async () => {
app.close(); await app.close();
}); });

View File

@ -77,7 +77,7 @@ e2e('should leave a workspace', async t => {
userId: u2.id, userId: u2.id,
}); });
app.switchUser(u2.id); await app.switchUser(u2.id);
const { leaveWorkspace } = await app.gql({ const { leaveWorkspace } = await app.gql({
query: leaveWorkspaceMutation, query: leaveWorkspaceMutation,
variables: { variables: {
@ -182,7 +182,7 @@ e2e('should invite a user by link', async t => {
}, },
}); });
app.switchUser(u2); await app.switchUser(u2);
const accept = await app.gql({ const accept = await app.gql({
query: acceptInviteByInviteIdMutation, query: acceptInviteByInviteIdMutation,
variables: { variables: {
@ -192,7 +192,7 @@ e2e('should invite a user by link', async t => {
}); });
t.true(accept.acceptInviteById, 'failed to accept invite'); t.true(accept.acceptInviteById, 'failed to accept invite');
app.switchUser(owner); await app.switchUser(owner);
const invite2 = await app.gql({ const invite2 = await app.gql({
query: inviteByEmailMutation, query: inviteByEmailMutation,
variables: { variables: {
@ -233,7 +233,7 @@ e2e('should send invitation notification and leave email', async t => {
t.is(invitationNotification.payload.inviterId, owner.id); t.is(invitationNotification.payload.inviterId, owner.id);
t.is(invitationNotification.payload.inviteId, invite.invite); t.is(invitationNotification.payload.inviteId, invite.invite);
app.switchUser(u2); await app.switchUser(u2);
const accept = await app.gql({ const accept = await app.gql({
query: acceptInviteByInviteIdMutation, query: acceptInviteByInviteIdMutation,
variables: { variables: {

View File

@ -42,7 +42,7 @@ export class MockCopilotProvider extends OpenAIProvider {
model: string = 'test', model: string = 'test',
options: CopilotChatOptions = {} options: CopilotChatOptions = {}
): Promise<string> { ): Promise<string> {
this.checkParams({ messages, model, options }); await this.checkParams({ messages, model, options });
// make some time gap for history test case // make some time gap for history test case
await sleep(100); await sleep(100);
return 'generate text to text'; return 'generate text to text';
@ -53,7 +53,7 @@ export class MockCopilotProvider extends OpenAIProvider {
model: string = 'gpt-4.1-mini', model: string = 'gpt-4.1-mini',
options: CopilotChatOptions = {} options: CopilotChatOptions = {}
): AsyncIterable<string> { ): AsyncIterable<string> {
this.checkParams({ messages, model, options }); await this.checkParams({ messages, model, options });
// make some time gap for history test case // make some time gap for history test case
await sleep(100); await sleep(100);
@ -74,7 +74,7 @@ export class MockCopilotProvider extends OpenAIProvider {
options: CopilotEmbeddingOptions = { dimensions: DEFAULT_DIMENSIONS } options: CopilotEmbeddingOptions = { dimensions: DEFAULT_DIMENSIONS }
): Promise<number[][]> { ): Promise<number[][]> {
messages = Array.isArray(messages) ? messages : [messages]; messages = Array.isArray(messages) ? messages : [messages];
this.checkParams({ embeddings: messages, model, options }); await this.checkParams({ embeddings: messages, model, options });
// make some time gap for history test case // make some time gap for history test case
await sleep(100); await sleep(100);

View File

@ -90,9 +90,14 @@ const init = async (
const workspace = await createWorkspace(app); const workspace = await createWorkspace(app);
const teamWorkspace = await createWorkspace(app); const teamWorkspace = await createWorkspace(app);
{ {
models.workspaceFeature.add(teamWorkspace.id, 'team_plan_v1', 'test', { await models.workspaceFeature.add(
memberLimit, teamWorkspace.id,
}); 'team_plan_v1',
'test',
{
memberLimit,
}
);
} }
const invite = async ( const invite = async (
@ -104,29 +109,29 @@ const init = async (
{ {
// normal workspace // normal workspace
app.switchUser(owner); await app.switchUser(owner);
const inviteId = await inviteUser( const inviteId = await inviteUser(
app, app,
workspace.id, workspace.id,
member.email, member.email,
shouldSendEmail shouldSendEmail
); );
app.switchUser(member); await app.switchUser(member);
await acceptInviteById(app, workspace.id, inviteId, shouldSendEmail); await acceptInviteById(app, workspace.id, inviteId, shouldSendEmail);
} }
{ {
// team workspace // team workspace
app.switchUser(owner); await app.switchUser(owner);
const inviteId = await inviteUser( const inviteId = await inviteUser(
app, app,
teamWorkspace.id, teamWorkspace.id,
member.email, member.email,
shouldSendEmail shouldSendEmail
); );
app.switchUser(member); await app.switchUser(member);
await acceptInviteById(app, teamWorkspace.id, inviteId, shouldSendEmail); await acceptInviteById(app, teamWorkspace.id, inviteId, shouldSendEmail);
app.switchUser(owner); await app.switchUser(owner);
await grantMember(app, teamWorkspace.id, member.id, permission); await grantMember(app, teamWorkspace.id, member.id, permission);
} }
@ -143,7 +148,7 @@ const init = async (
members.push(member); members.push(member);
} }
app.switchUser(owner); await app.switchUser(owner);
const invites = await inviteUsers( const invites = await inviteUsers(
app, app,
teamWorkspace.id, teamWorkspace.id,
@ -154,7 +159,7 @@ const init = async (
}; };
const getCreateInviteLinkFetcher = async (ws: WorkspaceType) => { const getCreateInviteLinkFetcher = async (ws: WorkspaceType) => {
app.switchUser(owner); await app.switchUser(owner);
const { link } = await createInviteLink(app, ws.id, 'OneDay'); const { link } = await createInviteLink(app, ws.id, 'OneDay');
const inviteId = link.split('/').pop()!; const inviteId = link.split('/').pop()!;
return [ return [
@ -165,7 +170,7 @@ const init = async (
return member; return member;
}, },
async (userId: string) => { async (userId: string) => {
app.switchUser(userId); await app.switchUser(userId);
await acceptInviteById(app, ws.id, inviteId, false); await acceptInviteById(app, ws.id, inviteId, false);
}, },
] as const; ] as const;
@ -183,7 +188,7 @@ const init = async (
WorkspaceRole.External WorkspaceRole.External
); );
app.switchUser(owner.id); await app.switchUser(owner.id);
return { return {
invite, invite,
inviteBatch, inviteBatch,
@ -204,13 +209,13 @@ test('should be able to invite multiple users', async t => {
{ {
// no permission // no permission
app.switchUser(read); await app.switchUser(read);
await t.throwsAsync( await t.throwsAsync(
inviteUsers(app, ws.id, ['test@affine.pro']), inviteUsers(app, ws.id, ['test@affine.pro']),
{ instanceOf: Error }, { instanceOf: Error },
'should throw error if not manager' 'should throw error if not manager'
); );
app.switchUser(write); await app.switchUser(write);
await t.throwsAsync( await t.throwsAsync(
inviteUsers(app, ws.id, ['test@affine.pro']), inviteUsers(app, ws.id, ['test@affine.pro']),
{ instanceOf: Error }, { instanceOf: Error },
@ -222,13 +227,13 @@ test('should be able to invite multiple users', async t => {
// manager // manager
const m1 = await app.signupV1('m1@affine.pro'); const m1 = await app.signupV1('m1@affine.pro');
const m2 = await app.signupV1('m2@affine.pro'); const m2 = await app.signupV1('m2@affine.pro');
app.switchUser(owner); await app.switchUser(owner);
t.is( t.is(
(await inviteUsers(app, ws.id, [m1.email])).length, (await inviteUsers(app, ws.id, [m1.email])).length,
1, 1,
'should be able to invite user' 'should be able to invite user'
); );
app.switchUser(admin); await app.switchUser(admin);
t.is( t.is(
(await inviteUsers(app, ws.id, [m2.email])).length, (await inviteUsers(app, ws.id, [m2.email])).length,
1, 1,
@ -263,7 +268,7 @@ test('should be able to check seat limit', async t => {
{ message: 'You have exceeded your workspace member quota.' }, { message: 'You have exceeded your workspace member quota.' },
'should throw error if exceed member limit' 'should throw error if exceed member limit'
); );
models.workspaceFeature.add(ws.id, 'team_plan_v1', 'test', { await models.workspaceFeature.add(ws.id, 'team_plan_v1', 'test', {
memberLimit: 6, memberLimit: 6,
}); });
await t.notThrowsAsync( await t.notThrowsAsync(
@ -310,14 +315,14 @@ test('should be able to grant team member permission', async t => {
const { app, models } = t.context; const { app, models } = t.context;
const { owner, teamWorkspace: ws, write, read } = await init(app); const { owner, teamWorkspace: ws, write, read } = await init(app);
app.switchUser(read); await app.switchUser(read);
await t.throwsAsync( await t.throwsAsync(
grantMember(app, ws.id, write.id, WorkspaceRole.Collaborator), grantMember(app, ws.id, write.id, WorkspaceRole.Collaborator),
{ instanceOf: Error }, { instanceOf: Error },
'should throw error if not owner' 'should throw error if not owner'
); );
app.switchUser(write); await app.switchUser(write);
await t.throwsAsync( await t.throwsAsync(
grantMember(app, ws.id, read.id, WorkspaceRole.Collaborator), grantMember(app, ws.id, read.id, WorkspaceRole.Collaborator),
{ instanceOf: Error }, { instanceOf: Error },
@ -326,7 +331,7 @@ test('should be able to grant team member permission', async t => {
{ {
// owner should be able to grant permission // owner should be able to grant permission
app.switchUser(owner); await app.switchUser(owner);
t.true( t.true(
(await models.workspaceUser.get(ws.id, read.id))?.type === (await models.workspaceUser.get(ws.id, read.id))?.type ===
WorkspaceRole.Collaborator, WorkspaceRole.Collaborator,
@ -348,24 +353,24 @@ test('should be able to leave workspace', async t => {
const { app } = t.context; const { app } = t.context;
const { owner, teamWorkspace: ws, admin, write, read } = await init(app); const { owner, teamWorkspace: ws, admin, write, read } = await init(app);
app.switchUser(owner); await app.switchUser(owner);
await t.throwsAsync(leaveWorkspace(app, ws.id), { await t.throwsAsync(leaveWorkspace(app, ws.id), {
message: 'Owner can not leave the workspace.', message: 'Owner can not leave the workspace.',
}); });
app.switchUser(admin); await app.switchUser(admin);
t.true( t.true(
await leaveWorkspace(app, ws.id), await leaveWorkspace(app, ws.id),
'admin should be able to leave workspace' 'admin should be able to leave workspace'
); );
app.switchUser(write); await app.switchUser(write);
t.true( t.true(
await leaveWorkspace(app, ws.id), await leaveWorkspace(app, ws.id),
'write should be able to leave workspace' 'write should be able to leave workspace'
); );
app.switchUser(read); await app.switchUser(read);
t.true( t.true(
await leaveWorkspace(app, ws.id), await leaveWorkspace(app, ws.id),
'read should be able to leave workspace' 'read should be able to leave workspace'
@ -378,7 +383,7 @@ test('should be able to revoke team member', async t => {
{ {
// no permission // no permission
app.switchUser(read); await app.switchUser(read);
await t.throwsAsync( await t.throwsAsync(
revokeUser(app, ws.id, read.id), revokeUser(app, ws.id, read.id),
{ instanceOf: Error }, { instanceOf: Error },
@ -393,7 +398,7 @@ test('should be able to revoke team member', async t => {
{ {
// manager // manager
app.switchUser(admin); await app.switchUser(admin);
t.true( t.true(
await revokeUser(app, ws.id, read.id), await revokeUser(app, ws.id, read.id),
'admin should be able to revoke member' 'admin should be able to revoke member'
@ -405,7 +410,7 @@ test('should be able to revoke team member', async t => {
'should not be able to revoke themselves' 'should not be able to revoke themselves'
); );
app.switchUser(owner); await app.switchUser(owner);
t.true( t.true(
await revokeUser(app, ws.id, write.id), await revokeUser(app, ws.id, write.id),
'owner should be able to revoke member' 'owner should be able to revoke member'
@ -416,7 +421,7 @@ test('should be able to revoke team member', async t => {
}); });
await revokeUser(app, ws.id, admin.id); await revokeUser(app, ws.id, admin.id);
app.switchUser(admin); await app.switchUser(admin);
await t.throwsAsync( await t.throwsAsync(
revokeUser(app, ws.id, read.id), revokeUser(app, ws.id, read.id),
{ instanceOf: Error }, { instanceOf: Error },
@ -441,7 +446,7 @@ test('should be able to manage invite link', async t => {
[tws, [owner, admin]], [tws, [owner, admin]],
] as const) { ] as const) {
for (const manager of managers) { for (const manager of managers) {
app.switchUser(manager.id); await app.switchUser(manager.id);
const { link } = await createInviteLink(app, workspace.id, 'OneDay'); const { link } = await createInviteLink(app, workspace.id, 'OneDay');
const { link: currLink } = await getInviteLink(app, workspace.id); const { link: currLink } = await getInviteLink(app, workspace.id);
t.is(link, currLink, 'should be able to get invite link'); t.is(link, currLink, 'should be able to get invite link');
@ -453,7 +458,7 @@ test('should be able to manage invite link', async t => {
} }
for (const collaborator of [write, read]) { for (const collaborator of [write, read]) {
app.switchUser(collaborator.id); await app.switchUser(collaborator.id);
await t.throwsAsync( await t.throwsAsync(
createInviteLink(app, workspace.id, 'OneDay'), createInviteLink(app, workspace.id, 'OneDay'),
{ instanceOf: Error }, { instanceOf: Error },
@ -478,7 +483,7 @@ test('should be able to approve team member', async t => {
const { teamWorkspace: tws, owner, admin, write, read } = await init(app, 6); const { teamWorkspace: tws, owner, admin, write, read } = await init(app, 6);
{ {
app.switchUser(owner); await app.switchUser(owner);
const { link } = await createInviteLink(app, tws.id, 'OneDay'); const { link } = await createInviteLink(app, tws.id, 'OneDay');
const inviteId = link.split('/').pop()!; const inviteId = link.split('/').pop()!;
@ -488,7 +493,7 @@ test('should be able to approve team member', async t => {
'should be able to accept invite' 'should be able to accept invite'
); );
app.switchUser(owner); await app.switchUser(owner);
const { members } = await getWorkspace(app, tws.id); const { members } = await getWorkspace(app, tws.id);
const memberInvite = members.find(m => m.id === member.id)!; const memberInvite = members.find(m => m.id === member.id)!;
t.is(memberInvite.status, 'UnderReview', 'should be under review'); t.is(memberInvite.status, 'UnderReview', 'should be under review');
@ -509,21 +514,21 @@ test('should be able to approve team member', async t => {
} }
{ {
app.switchUser(admin); await app.switchUser(admin);
await t.throwsAsync( await t.throwsAsync(
approveMember(app, tws.id, 'not_exists_id'), approveMember(app, tws.id, 'not_exists_id'),
{ instanceOf: Error }, { instanceOf: Error },
'should throw error if member not exists' 'should throw error if member not exists'
); );
app.switchUser(write); await app.switchUser(write);
await t.throwsAsync( await t.throwsAsync(
approveMember(app, tws.id, 'not_exists_id'), approveMember(app, tws.id, 'not_exists_id'),
{ instanceOf: Error }, { instanceOf: Error },
'should throw error if not manager' 'should throw error if not manager'
); );
app.switchUser(read); await app.switchUser(read);
await t.throwsAsync( await t.throwsAsync(
approveMember(app, tws.id, 'not_exists_id'), approveMember(app, tws.id, 'not_exists_id'),
{ instanceOf: Error }, { instanceOf: Error },
@ -547,7 +552,7 @@ test('should be able to invite by link', async t => {
const member = await app.signup(); const member = await app.signup();
{ {
// check invite link // check invite link
app.switchUser(member); await app.switchUser(member);
const info = await getInviteInfo(app, inviteId); const info = await getInviteInfo(app, inviteId);
t.is(info.workspace.id, ws.id, 'should be able to get invite info'); t.is(info.workspace.id, ws.id, 'should be able to get invite info');
t.falsy(info.status); t.falsy(info.status);
@ -601,7 +606,7 @@ test('should be able to invite by link', async t => {
'should not change status' 'should not change status'
); );
models.workspaceFeature.add(tws.id, 'team_plan_v1', 'test', { await models.workspaceFeature.add(tws.id, 'team_plan_v1', 'test', {
memberLimit: 6, memberLimit: 6,
}); });
await models.workspaceUser.refresh(tws.id, 6); await models.workspaceUser.refresh(tws.id, 6);
@ -616,7 +621,7 @@ test('should be able to invite by link', async t => {
'should not change status' 'should not change status'
); );
models.workspaceFeature.add(tws.id, 'team_plan_v1', 'test', { await models.workspaceFeature.add(tws.id, 'team_plan_v1', 'test', {
memberLimit: 7, memberLimit: 7,
}); });
await models.workspaceUser.refresh(tws.id, 7); await models.workspaceUser.refresh(tws.id, 7);
@ -670,7 +675,7 @@ test('should be able to emit events and send notifications', async t => {
const { teamWorkspace: tws, owner, createInviteLink } = await init(app); const { teamWorkspace: tws, owner, createInviteLink } = await init(app);
const [, invite] = await createInviteLink(tws); const [, invite] = await createInviteLink(tws);
const user = await invite('m3@affine.pro'); const user = await invite('m3@affine.pro');
app.switchUser(owner); await app.switchUser(owner);
const { members } = await getWorkspace(app, tws.id); const { members } = await getWorkspace(app, tws.id);
const memberInvite = members.find(m => m.id === user.id)!; const memberInvite = members.find(m => m.id === user.id)!;
const requestRequestNotification = app.queue.last( const requestRequestNotification = app.queue.last(
@ -688,7 +693,7 @@ test('should be able to emit events and send notifications', async t => {
'should send review request notification' 'should send review request notification'
); );
app.switchUser(owner); await app.switchUser(owner);
await revokeUser(app, tws.id, user.id); await revokeUser(app, tws.id, user.id);
const requestDeclinedNotification = app.queue.last( const requestDeclinedNotification = app.queue.last(
'notification.sendInvitationReviewDeclined' 'notification.sendInvitationReviewDeclined'
@ -735,7 +740,7 @@ test('should be able to emit events and send notifications', async t => {
'should emit owner transferred event' 'should emit owner transferred event'
); );
app.switchUser(read); await app.switchUser(read);
await revokeMember(app, tws.id, owner.id); await revokeMember(app, tws.id, owner.id);
const [memberRemoved, memberUpdated] = event.emit const [memberRemoved, memberUpdated] = event.emit
.getCalls() .getCalls()
@ -777,7 +782,7 @@ test('should be able to grant and revoke users role in page', async t => {
} = await init(app, 5); } = await init(app, 5);
const docId = nanoid(); const docId = nanoid();
app.switchUser(admin); await app.switchUser(admin);
const res = await grantDocUserRoles( const res = await grantDocUserRoles(
app, app,
ws.id, ws.id,
@ -795,7 +800,7 @@ test('should be able to grant and revoke users role in page', async t => {
await grantDocUserRoles(app, ws.id, docId, [read.id], DocRole.Reader); await grantDocUserRoles(app, ws.id, docId, [read.id], DocRole.Reader);
// read still be the Manager of this doc // read still be the Manager of this doc
app.switchUser(read); await app.switchUser(read);
const res = await grantDocUserRoles( const res = await grantDocUserRoles(
app, app,
ws.id, ws.id,
@ -807,7 +812,7 @@ test('should be able to grant and revoke users role in page', async t => {
grantDocUserRoles: true, grantDocUserRoles: true,
}); });
app.switchUser(admin); await app.switchUser(admin);
const docUsersList = await docGrantedUsersList(app, ws.id, docId); const docUsersList = await docGrantedUsersList(app, ws.id, docId);
t.is(docUsersList.workspace.doc.grantedUsersList.totalCount, 3); t.is(docUsersList.workspace.doc.grantedUsersList.totalCount, 3);
const externalRole = docUsersList.workspace.doc.grantedUsersList.edges.find( const externalRole = docUsersList.workspace.doc.grantedUsersList.edges.find(
@ -821,7 +826,7 @@ test('should be able to change the default role in page', async t => {
const { app } = t.context; const { app } = t.context;
const { teamWorkspace: ws, admin } = await init(app, 5); const { teamWorkspace: ws, admin } = await init(app, 5);
const docId = nanoid(); const docId = nanoid();
app.switchUser(admin); await app.switchUser(admin);
const res = await updateDocDefaultRole(app, ws.id, docId, DocRole.Reader); const res = await updateDocDefaultRole(app, ws.id, docId, DocRole.Reader);
t.deepEqual(res, { t.deepEqual(res, {
@ -840,7 +845,7 @@ test('default page role should be able to override the workspace role', async t
const docId = nanoid(); const docId = nanoid();
app.switchUser(admin); await app.switchUser(admin);
const res = await updateDocDefaultRole( const res = await updateDocDefaultRole(
app, app,
workspace.id, workspace.id,
@ -854,7 +859,7 @@ test('default page role should be able to override the workspace role', async t
// reader can manage the page if the page default role is Manager // reader can manage the page if the page default role is Manager
{ {
app.switchUser(read); await app.switchUser(read);
const readerRes = await updateDocDefaultRole( const readerRes = await updateDocDefaultRole(
app, app,
workspace.id, workspace.id,
@ -869,7 +874,7 @@ test('default page role should be able to override the workspace role', async t
// external can't manage the page even if the page default role is Manager // external can't manage the page even if the page default role is Manager
{ {
app.switchUser(external); await app.switchUser(external);
await t.throwsAsync( await t.throwsAsync(
updateDocDefaultRole(app, workspace.id, docId, DocRole.Manager), updateDocDefaultRole(app, workspace.id, docId, DocRole.Manager),
{ {
@ -884,7 +889,7 @@ test('should be able to grant and revoke doc user role', async t => {
const { teamWorkspace: ws, admin, read, external } = await init(app, 5); const { teamWorkspace: ws, admin, read, external } = await init(app, 5);
const docId = nanoid(); const docId = nanoid();
app.switchUser(admin); await app.switchUser(admin);
const res = await grantDocUserRoles( const res = await grantDocUserRoles(
app, app,
ws.id, ws.id,
@ -899,7 +904,7 @@ test('should be able to grant and revoke doc user role', async t => {
// external user can never be able to manage the page // external user can never be able to manage the page
{ {
app.switchUser(external); await app.switchUser(external);
await t.throwsAsync( await t.throwsAsync(
grantDocUserRoles(app, ws.id, docId, [read.id], DocRole.Manager), grantDocUserRoles(app, ws.id, docId, [read.id], DocRole.Manager),
{ {
@ -910,7 +915,7 @@ test('should be able to grant and revoke doc user role', async t => {
// revoke the role of the external user // revoke the role of the external user
{ {
app.switchUser(admin); await app.switchUser(admin);
const revokeRes = await revokeDocUserRoles(app, ws.id, docId, external.id); const revokeRes = await revokeDocUserRoles(app, ws.id, docId, external.id);
t.deepEqual(revokeRes, { t.deepEqual(revokeRes, {
@ -918,7 +923,7 @@ test('should be able to grant and revoke doc user role', async t => {
}); });
// external user can't manage the page // external user can't manage the page
app.switchUser(external); await app.switchUser(external);
await t.throwsAsync(revokeDocUserRoles(app, ws.id, docId, read.id), { await t.throwsAsync(revokeDocUserRoles(app, ws.id, docId, read.id), {
message: `You do not have permission to perform Doc.Users.Manage action on doc ${docId}.`, message: `You do not have permission to perform Doc.Users.Manage action on doc ${docId}.`,
}); });
@ -930,7 +935,7 @@ test('update page default role should throw error if the space does not exist',
const { admin } = await init(app, 5); const { admin } = await init(app, 5);
const docId = nanoid(); const docId = nanoid();
const nonExistWorkspaceId = 'non-exist-workspace'; const nonExistWorkspaceId = 'non-exist-workspace';
app.switchUser(admin); await app.switchUser(admin);
await t.throwsAsync( await t.throwsAsync(
updateDocDefaultRole(app, nonExistWorkspaceId, docId, DocRole.Manager), updateDocDefaultRole(app, nonExistWorkspaceId, docId, DocRole.Manager),
{ {

View File

@ -47,7 +47,7 @@ const assertAndSnapshotRaw = async (
.send(options?.body) .send(options?.body)
.expect(status) .expect(status)
.expect(checker); .expect(checker);
t.notThrowsAsync(res, message); await t.notThrowsAsync(res, message);
t.snapshot((await res).body); t.snapshot((await res).body);
}; };

View File

@ -122,7 +122,7 @@ test('should be able to get workspace doc', async t => {
const u1 = await app.signupV1('u1@affine.pro'); const u1 = await app.signupV1('u1@affine.pro');
const u2 = await app.signupV1('u2@affine.pro'); const u2 = await app.signupV1('u2@affine.pro');
app.switchUser(u1.id); await app.switchUser(u1.id);
const workspace = await createWorkspace(app); const workspace = await createWorkspace(app);
const res1 = await app const res1 = await app
@ -136,7 +136,7 @@ test('should be able to get workspace doc', async t => {
'failed to get doc with u1 token' 'failed to get doc with u1 token'
); );
app.switchUser(u2.id); await app.switchUser(u2.id);
await app await app
.GET(`/api/workspaces/${workspace.id}/docs/${workspace.id}`) .GET(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
.expect(403); .expect(403);

View File

@ -28,12 +28,12 @@ async function randomPut(
): Promise<string> { ): Promise<string> {
const key = prefix + 'test-key-' + Math.random().toString(16).substring(2, 8); const key = prefix + 'test-key-' + Math.random().toString(16).substring(2, 8);
const body = Buffer.from(key); const body = Buffer.from(key);
provider.put(key, body); await provider.put(key, body);
return key; return key;
} }
test.after.always(() => { test.after.always(() => {
fs.rm(config.path, { recursive: true }); fs.rm(config.path, { recursive: true }).catch(console.error);
}); });
test('put & get', async t => { test('put & get', async t => {

View File

@ -22,12 +22,18 @@ describe('async-queue', () => {
let v = -1; let v = -1;
// setup 2 pop tasks // setup 2 pop tasks
queue.next().then(next => { queue
v = next; .next()
}); .then(next => {
queue.next().then(next => { v = next;
v = next; })
}); .catch(console.error);
queue
.next()
.then(next => {
v = next;
})
.catch(console.error);
// Wait for 100ms // Wait for 100ms
await new Promise(resolve => setTimeout(resolve, 100)); await new Promise(resolve => setTimeout(resolve, 100));

View File

@ -82,7 +82,7 @@ const AudioWrapper = () => {
e.preventDefault(); e.preventDefault();
const file = e.dataTransfer.files[0]; const file = e.dataTransfer.files[0];
if (file && file.type.startsWith('audio/')) { if (file && file.type.startsWith('audio/')) {
handleFileChange(file); handleFileChange(file).catch(console.error);
} }
}, },
[handleFileChange] [handleFileChange]
@ -92,7 +92,7 @@ const AudioWrapper = () => {
(e: React.ChangeEvent<HTMLInputElement>) => { (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
if (file) { if (file) {
handleFileChange(file); handleFileChange(file).catch(console.error);
} }
}, },
[handleFileChange] [handleFileChange]

View File

@ -113,7 +113,7 @@ test.describe('AIBasic/Chat', () => {
}, },
]); ]);
expect(page.getByTestId('chat-action-list')).toBeVisible(); await expect(page.getByTestId('chat-action-list')).toBeVisible();
await utils.chatPanel.makeChat(page, 'Nice to meet you'); await utils.chatPanel.makeChat(page, 'Nice to meet you');
await utils.chatPanel.waitForHistory(page, [ await utils.chatPanel.waitForHistory(page, [
{ {
@ -458,7 +458,7 @@ test.describe('AIBasic/Chat', () => {
await sendButton.click(); await sendButton.click();
await expect(page.getByTestId('sidebar-tab-content-chat')).toBeVisible(); await expect(page.getByTestId('sidebar-tab-content-chat')).toBeVisible();
expect(await page.locator('chat-content-images')).toBeVisible(); await expect(page.locator('chat-content-images')).toBeVisible();
await utils.chatPanel.waitForHistory(page, [ await utils.chatPanel.waitForHistory(page, [
{ {
role: 'user', role: 'user',

File diff suppressed because one or more lines are too long

View File

@ -44,7 +44,7 @@ test.describe('AIInsertion/AddToEdgelessAsNote', () => {
// Delete default note // Delete default note
await (await page.waitForSelector('affine-edgeless-note')).click(); await (await page.waitForSelector('affine-edgeless-note')).click();
page.keyboard.press('Delete'); await page.keyboard.press('Delete');
await utils.chatPanel.openChatPanel(page); await utils.chatPanel.openChatPanel(page);
await utils.chatPanel.makeChat(page, 'Hello'); await utils.chatPanel.makeChat(page, 'Hello');

View File

@ -170,7 +170,7 @@ test.describe('AIInsertion/Insert', () => {
// Delete default note // Delete default note
await (await page.waitForSelector('affine-edgeless-note')).click(); await (await page.waitForSelector('affine-edgeless-note')).click();
page.keyboard.press('Delete'); await page.keyboard.press('Delete');
await utils.chatPanel.openChatPanel(page); await utils.chatPanel.openChatPanel(page);
await utils.chatPanel.makeChat(page, 'Hello'); await utils.chatPanel.makeChat(page, 'Hello');

View File

@ -30,6 +30,10 @@ export class ChatPanelUtils {
await page.getByTestId('right-sidebar-toggle').click({ await page.getByTestId('right-sidebar-toggle').click({
delay: 200, delay: 200,
}); });
await page.waitForTimeout(500); // wait the sidebar stable
}
if (await page.getByTestId('notification-close-button').isVisible()) {
await page.getByTestId('notification-close-button').click();
} }
await page.getByTestId('sidebar-tab-chat').click(); await page.getByTestId('sidebar-tab-chat').click();
await expect(page.getByTestId('sidebar-tab-content-chat')).toBeVisible(); await expect(page.getByTestId('sidebar-tab-content-chat')).toBeVisible();
@ -291,14 +295,14 @@ export class ChatPanelUtils {
} }
public static async enableNetworkSearch(page: Page) { public static async enableNetworkSearch(page: Page) {
const networkSearch = await page.getByTestId('chat-network-search'); const networkSearch = page.getByTestId('chat-network-search');
if ((await networkSearch.getAttribute('data-active')) === 'false') { if ((await networkSearch.getAttribute('data-active')) === 'false') {
await networkSearch.click(); await networkSearch.click();
} }
} }
public static async disableNetworkSearch(page: Page) { public static async disableNetworkSearch(page: Page) {
const networkSearch = await page.getByTestId('chat-network-search'); const networkSearch = page.getByTestId('chat-network-search');
if ((await networkSearch.getAttribute('data-active')) === 'true') { if ((await networkSearch.getAttribute('data-active')) === 'true') {
await networkSearch.click(); await networkSearch.click();
} }

View File

@ -51,7 +51,7 @@ export class EditorUtils {
public static async switchToEdgelessMode(page: Page) { public static async switchToEdgelessMode(page: Page) {
const editor = await page.waitForSelector('page-editor'); const editor = await page.waitForSelector('page-editor');
await page.getByTestId('switch-edgeless-mode-button').click(); await page.getByTestId('switch-edgeless-mode-button').click();
editor.waitForElementState('hidden'); await editor.waitForElementState('hidden');
await page.waitForSelector('edgeless-editor'); await page.waitForSelector('edgeless-editor');
try { try {
const edgelessNotificationClose = page.getByTestId( const edgelessNotificationClose = page.getByTestId(
@ -408,9 +408,7 @@ export class EditorUtils {
checkCodeError: this.createAction(page, () => checkCodeError: this.createAction(page, () =>
page.getByTestId('action-check-code-error').click() page.getByTestId('action-check-code-error').click()
), ),
continueWithAi: async () => { continueWithAi: () => page.getByTestId('action-continue-with-ai').click(),
page.getByTestId('action-continue-with-ai').click();
},
continueWriting: this.createAction(page, () => continueWriting: this.createAction(page, () =>
page.getByTestId('action-continue-writing').click() page.getByTestId('action-continue-writing').click()
), ),
@ -596,9 +594,7 @@ export class EditorUtils {
checkCodeError: this.createAction(page, () => checkCodeError: this.createAction(page, () =>
page.getByTestId('action-check-code-error').click() page.getByTestId('action-check-code-error').click()
), ),
continueWithAi: async () => { continueWithAi: () => page.getByTestId('action-continue-with-ai').click(),
page.getByTestId('action-continue-with-ai').click();
},
continueWriting: this.createAction(page, () => continueWriting: this.createAction(page, () =>
page.getByTestId('action-continue-writing').click() page.getByTestId('action-continue-writing').click()
), ),

View File

@ -164,7 +164,7 @@ test('can sync svg between different browsers', async ({ page, browser }) => {
const fileChooserPromise = page.waitForEvent('filechooser'); const fileChooserPromise = page.waitForEvent('filechooser');
await page.keyboard.press('Enter', { delay: 50 }); await page.keyboard.press('Enter', { delay: 50 });
const fileChooser = await fileChooserPromise; const fileChooser = await fileChooserPromise;
fileChooser.setFiles(Path.dir(import.meta.url).join('logo.svg').value); await fileChooser.setFiles(Path.dir(import.meta.url).join('logo.svg').value);
await expect(image).toBeVisible(); await expect(image).toBeVisible();
// the user should see the svg // the user should see the svg

View File

@ -28,6 +28,6 @@ test('import from template should work', async ({ page }) => {
const btn = page.getByTestId('import-template-to-workspace-btn'); const btn = page.getByTestId('import-template-to-workspace-btn');
await btn.isVisible(); await btn.isVisible();
btn.click(); await btn.click();
await waitForEditorLoad(page); await waitForEditorLoad(page);
}); });

View File

@ -48,7 +48,7 @@ test.describe('split list', () => {
listLocator.nth(2).locator('.affine-list-block__numbered') listLocator.nth(2).locator('.affine-list-block__numbered')
).toHaveText('2.'); ).toHaveText('2.');
await expect(listLocator.nth(3).locator('rich-text')).toHaveText('ddd'); await expect(listLocator.nth(3).locator('rich-text')).toHaveText('ddd');
expect( await expect(
listLocator.nth(3).locator('.affine-list-block__numbered') listLocator.nth(3).locator('.affine-list-block__numbered')
).toHaveText('3.'); ).toHaveText('3.');

View File

@ -137,15 +137,14 @@ test.skip('create multi workspace in the workspace list', async ({
await expect(workspaceCards).toHaveCount(3); await expect(workspaceCards).toHaveCount(3);
} }
const workspaceChangePromise = page.evaluate(() => { await page.getByTestId('draggable-item').nth(2).click();
new Promise(resolve => { await page.evaluate(async () => {
await new Promise(resolve => {
window.addEventListener('affine:workspace:change', resolve, { window.addEventListener('affine:workspace:change', resolve, {
once: true, once: true,
}); });
}); });
}); });
await page.getByTestId('draggable-item').nth(2).click();
await workspaceChangePromise;
const nextWorkspace = await workspace.current(); const nextWorkspace = await workspace.current();

View File

@ -178,7 +178,7 @@ test('add mindmap into frame, then drag root node of mindmap out.', async ({
// drag out // drag out
{ {
const mindmapBound = await getSelectedBound(page); const mindmapBound = await getSelectedBound(page);
pressEscape(page); await pressEscape(page);
await clickView(page, [ await clickView(page, [
mindmapBound[0] + 10, mindmapBound[0] + 10,
mindmapBound[1] + 0.5 * mindmapBound[3], mindmapBound[1] + 0.5 * mindmapBound[3],

View File

@ -290,8 +290,8 @@ test.describe('lock', () => {
await selectAllByKeyboard(page); await selectAllByKeyboard(page);
await dragBetweenViewCoords(page, [100, 100], [150, 150]); await dragBetweenViewCoords(page, [100, 100], [150, 150]);
assertEdgelessElementBound(page, frame, [100, 100, 200, 200]); await assertEdgelessElementBound(page, frame, [100, 100, 200, 200]);
assertEdgelessElementBound(page, shape, [150, 150, 50, 50]); await assertEdgelessElementBound(page, shape, [150, 150, 50, 50]);
}); });
test('locked element should not be scalable and rotatable. unlocking will recover', async ({ test('locked element should not be scalable and rotatable. unlocking will recover', async ({

View File

@ -196,7 +196,7 @@ test.describe('Embed synced doc in edgeless mode', () => {
await expect(edgelessNotes).toHaveCount(2); await expect(edgelessNotes).toHaveCount(2);
expect(await getSelectedIds(page)).toHaveLength(1); expect(await getSelectedIds(page)).toHaveLength(1);
expect(await getSelectedIds(page)).not.toContain(prevIds); expect(await getSelectedIds(page)).not.toContain(prevIds);
expect(edgelessNotes.last()).toBeVisible(); await expect(edgelessNotes.last()).toBeVisible();
const noteBound = await getSelectedBound(page); const noteBound = await getSelectedBound(page);
expect(isIntersected(embedDocBound, noteBound)).toBe(false); expect(isIntersected(embedDocBound, noteBound)).toBe(false);

View File

@ -27,7 +27,7 @@ export async function initEmbedSyncedDocState(
} }
return await page.evaluate( return await page.evaluate(
({ data, option }) => { async ({ data, option }) => {
const createDoc = async ( const createDoc = async (
docId: string, docId: string,
title: string, title: string,
@ -69,11 +69,13 @@ export async function initEmbedSyncedDocState(
return note ?? null; return note ?? null;
}; };
const docIds = data.map(({ title, content }, index) => { const docIds = await Promise.all(
const id = index === 0 ? window.doc.id : `embed-doc-${index}`; data.map(async ({ title, content }, index) => {
createDoc(id, title, content); const id = index === 0 ? window.doc.id : `embed-doc-${index}`;
return id; await createDoc(id, title, content);
}); return id;
})
);
const { NoteBlockModel, NoteDisplayMode } = const { NoteBlockModel, NoteDisplayMode } =
window.$blocksuite.affineModel; window.$blocksuite.affineModel;

View File

@ -473,7 +473,7 @@ export async function createEdgelessNoteBlock(
) { ) {
await setEdgelessTool(page, 'note', undefined, editorIndex); await setEdgelessTool(page, 'note', undefined, editorIndex);
if (position.length === 4) { if (position.length === 4) {
dragView( await dragView(
page, page,
[position[0], position[1]], [position[0], position[1]],
[position[0] + position[2], position[1] + position[3]] [position[0] + position[2], position[1] + position[3]]

View File

@ -122,7 +122,7 @@ export const addCustomProperty = async (
root: Locator | Page, root: Locator | Page,
type: string type: string
) => { ) => {
ensureAddPropertyButtonVisible(page, root); await ensureAddPropertyButtonVisible(page, root);
await clickAddPropertyButton(root); await clickAddPropertyButton(root);
await page await page
.locator( .locator(