diff --git a/.eslintrc.js b/.eslintrc.js
index e13090b43d..bce0ab722a 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -272,6 +272,7 @@ const config = {
],
'@typescript-eslint/no-floating-promises': 0,
'@typescript-eslint/no-misused-promises': 0,
+ '@typescript-eslint/no-restricted-imports': 0,
},
},
],
diff --git a/.github/workflows/build-server.yml b/.github/workflows/build-server.yml
index c2d3441abf..d46b600784 100644
--- a/.github/workflows/build-server.yml
+++ b/.github/workflows/build-server.yml
@@ -118,7 +118,7 @@ jobs:
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
ENABLE_LOCAL_EMAIL: true
- OAUTH_EMAIL_LOGIN: affine
+ OAUTH_EMAIL_LOGIN: noreply@toeverything.info
OAUTH_EMAIL_PASSWORD: affine
- name: Upload server test coverage results
@@ -191,9 +191,6 @@ jobs:
env:
COVERAGE: true
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- ENABLE_LOCAL_EMAIL: true
- OAUTH_EMAIL_LOGIN: affine
- OAUTH_EMAIL_PASSWORD: affine
- name: Collect code coverage report
run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
diff --git a/apps/core/src/components/affine/setting-modal/account-setting/index.tsx b/apps/core/src/components/affine/setting-modal/account-setting/index.tsx
index 9942322eca..130fedcc90 100644
--- a/apps/core/src/components/affine/setting-modal/account-setting/index.tsx
+++ b/apps/core/src/components/affine/setting-modal/account-setting/index.tsx
@@ -44,7 +44,6 @@ export const UserAvatar = () => {
const handleUpdateUserAvatar = useCallback(
async (file: File) => {
await avatarTrigger({
- id: user.id,
avatar: file,
});
// XXX: This is a hack to force the user to update, since next-auth can not only use update function without params
diff --git a/apps/server/package.json b/apps/server/package.json
index 057d4e8a2d..8d41f78bfa 100644
--- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -81,6 +81,7 @@
"@types/cookie-parser": "^1.4.3",
"@types/engine.io": "^3.1.7",
"@types/express": "^4.17.17",
+ "@types/graphql-upload": "^16.0.1",
"@types/keyv": "^4.2.0",
"@types/lodash-es": "^4.17.9",
"@types/node": "^18.17.12",
@@ -104,19 +105,24 @@
},
"nodeArguments": [
"--loader",
- "ts-node/esm.mjs",
+ "ts-node/esm/transpile-only.mjs",
"--es-module-specifier-resolution",
"node"
],
"files": [
- "src/**/*.spec.ts"
+ "tests/**/*.spec.ts",
+ "tests/**/*.e2e.ts"
],
"require": [
"./src/prelude.ts"
],
"environmentVariables": {
- "TS_NODE_PROJECT": "./tsconfig.json",
- "NODE_ENV": "test"
+ "TS_NODE_PROJECT": "./tests/tsconfig.json",
+ "NODE_ENV": "test",
+ "ENABLE_LOCAL_EMAIL": "true",
+ "OAUTH_EMAIL_LOGIN": "noreply@toeverything.info",
+ "OAUTH_EMAIL_PASSWORD": "affine",
+ "OAUTH_EMAIL_SENDER": "noreply@toeverything.info"
}
},
"nodemonConfig": {
diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts
index 02e041bd86..4b83dbd446 100644
--- a/apps/server/src/index.ts
+++ b/apps/server/src/index.ts
@@ -19,7 +19,6 @@ import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-node';
import { PrismaInstrumentation } from '@prisma/instrumentation';
import cookieParser from 'cookie-parser';
import { static as staticMiddleware } from 'express';
-// @ts-expect-error graphql-upload is not typed
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import { AppModule } from './app';
diff --git a/apps/server/src/modules/auth/guard.ts b/apps/server/src/modules/auth/guard.ts
index f2f31cbb35..6b4532354d 100644
--- a/apps/server/src/modules/auth/guard.ts
+++ b/apps/server/src/modules/auth/guard.ts
@@ -16,8 +16,7 @@ import { NextAuthOptionsProvide } from './next-auth-options';
import { AuthService } from './service';
export function getUserFromContext(context: ExecutionContext) {
- const req = getRequestResponseFromContext(context).req;
- return req.user;
+ return getRequestResponseFromContext(context).req.user;
}
/**
diff --git a/apps/server/src/modules/users/resolver.ts b/apps/server/src/modules/users/resolver.ts
index e9abdfd9e6..f7e629030c 100644
--- a/apps/server/src/modules/users/resolver.ts
+++ b/apps/server/src/modules/users/resolver.ts
@@ -15,7 +15,6 @@ import {
Resolver,
} from '@nestjs/graphql';
import type { User } from '@prisma/client';
-// @ts-expect-error graphql-upload is not typed
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import { PrismaService } from '../../prisma/service';
@@ -141,17 +140,16 @@ export class UserResolver {
description: 'Upload user avatar',
})
async uploadAvatar(
- @Args('id') id: string,
+ @CurrentUser() user: UserType,
@Args({ name: 'avatar', type: () => GraphQLUpload })
avatar: FileUpload
) {
- const user = await this.users.findUserById(id);
if (!user) {
- throw new BadRequestException(`User ${id} not found`);
+ throw new BadRequestException(`User not found`);
}
- const url = await this.storage.uploadFile(`${id}-avatar`, avatar);
+ const url = await this.storage.uploadFile(`${user.id}-avatar`, avatar);
return this.prisma.user.update({
- where: { id },
+ where: { id: user.id },
data: { avatarUrl: url },
});
}
diff --git a/apps/server/src/modules/workspaces/resolver.ts b/apps/server/src/modules/workspaces/resolver.ts
index 43b3902d26..e83f2a97b5 100644
--- a/apps/server/src/modules/workspaces/resolver.ts
+++ b/apps/server/src/modules/workspaces/resolver.ts
@@ -26,7 +26,6 @@ import {
Resolver,
} from '@nestjs/graphql';
import type { User, Workspace } from '@prisma/client';
-// @ts-expect-error graphql-upload is not typed
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import { applyUpdate, Doc } from 'yjs';
@@ -648,10 +647,10 @@ export class WorkspaceResolver {
workspaceId,
},
});
- return (
- userWorkspace?.accepted &&
- (await this.permissions.grantPage(workspaceId, pageId))
- );
+ if (!userWorkspace?.accepted) {
+ throw new ForbiddenException('Permission denied');
+ }
+ return this.permissions.grantPage(workspaceId, pageId);
}
@Mutation(() => Boolean)
diff --git a/apps/server/src/schema.gql b/apps/server/src/schema.gql
index 1f70307a52..177f49b31c 100644
--- a/apps/server/src/schema.gql
+++ b/apps/server/src/schema.gql
@@ -190,7 +190,7 @@ type Mutation {
deleteBlob(workspaceId: String!, hash: String!): Boolean!
"""Upload user avatar"""
- uploadAvatar(id: String!, avatar: Upload!): UserType!
+ uploadAvatar(avatar: Upload!): UserType!
"""Remove user avatar"""
removeAvatar: RemoveAvatar!
diff --git a/apps/server/src/tests/app.e2e.ts b/apps/server/tests/app.e2e.ts
similarity index 92%
rename from apps/server/src/tests/app.e2e.ts
rename to apps/server/tests/app.e2e.ts
index 05763196b2..d5c713d530 100644
--- a/apps/server/src/tests/app.e2e.ts
+++ b/apps/server/tests/app.e2e.ts
@@ -8,12 +8,11 @@ import { hashSync } from '@node-rs/argon2';
import { User } from '@prisma/client';
import ava, { TestFn } from 'ava';
import { Express } from 'express';
-// @ts-expect-error graphql-upload is not typed
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import request from 'supertest';
-import { AppModule } from '../app';
-import { PrismaService } from '../prisma/service';
+import { AppModule } from '../src/app';
+import { PrismaService } from '../src/prisma/service';
const gql = '/graphql';
@@ -139,8 +138,8 @@ test('should be able to upload avatar and remove it', async t => {
'operations',
JSON.stringify({
name: 'uploadAvatar',
- query: `mutation uploadAvatar($id: String!, $avatar: Upload!) {
- uploadAvatar(id: $id, avatar: $avatar) {
+ query: `mutation uploadAvatar($avatar: Upload!) {
+ uploadAvatar(avatar: $avatar) {
id
name
avatarUrl
@@ -166,17 +165,14 @@ test('should be able to upload avatar and remove it', async t => {
query: `
mutation removeAvatar {
removeAvatar {
- id
- name
- avatarUrl
- email
+ success
}
}
`,
})
.expect(200)
.expect(res => {
- t.is(res.body.data.removeAvatar.avatarUrl, '');
+ t.is(res.body.data.removeAvatar.success, true);
});
});
diff --git a/apps/server/src/tests/auth.e2e.ts b/apps/server/tests/auth.e2e.ts
similarity index 94%
rename from apps/server/src/tests/auth.e2e.ts
rename to apps/server/tests/auth.e2e.ts
index 4c2517e961..95a69f5735 100644
--- a/apps/server/src/tests/auth.e2e.ts
+++ b/apps/server/tests/auth.e2e.ts
@@ -1,18 +1,19 @@
+import {
+ getCurrentMailMessageCount,
+ getLatestMailMessage,
+} from '@affine-test/kit/utils/cloud';
import type { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import ava, { TestFn } from 'ava';
-// @ts-expect-error graphql-upload is not typed
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
-import { AppModule } from '../app';
-import { MailService } from '../modules/auth/mailer';
-import { AuthService } from '../modules/auth/service';
+import { AppModule } from '../src/app';
+import { MailService } from '../src/modules/auth/mailer';
+import { AuthService } from '../src/modules/auth/service';
import {
changeEmail,
createWorkspace,
- getCurrentMailMessageCount,
- getLatestMailMessage,
sendChangeEmail,
sendVerifyChangeEmail,
signUp,
diff --git a/apps/server/src/tests/auth.spec.ts b/apps/server/tests/auth.spec.ts
similarity index 89%
rename from apps/server/src/tests/auth.spec.ts
rename to apps/server/tests/auth.spec.ts
index 93ea72ecbc..10a6a80d24 100644
--- a/apps/server/src/tests/auth.spec.ts
+++ b/apps/server/tests/auth.spec.ts
@@ -1,16 +1,16 @@
-///
+///
import { Test, TestingModule } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import test from 'ava';
-import { ConfigModule } from '../config';
-import { GqlModule } from '../graphql.module';
-import { MetricsModule } from '../metrics';
-import { AuthModule } from '../modules/auth';
-import { AuthResolver } from '../modules/auth/resolver';
-import { AuthService } from '../modules/auth/service';
-import { PrismaModule } from '../prisma';
-import { RateLimiterModule } from '../throttler';
+import { ConfigModule } from '../src/config';
+import { GqlModule } from '../src/graphql.module';
+import { MetricsModule } from '../src/metrics';
+import { AuthModule } from '../src/modules/auth';
+import { AuthResolver } from '../src/modules/auth/resolver';
+import { AuthService } from '../src/modules/auth/service';
+import { PrismaModule } from '../src/prisma';
+import { RateLimiterModule } from '../src/throttler';
let authService: AuthService;
let authResolver: AuthResolver;
diff --git a/apps/server/src/tests/config.spec.ts b/apps/server/tests/config.spec.ts
similarity index 93%
rename from apps/server/src/tests/config.spec.ts
rename to apps/server/tests/config.spec.ts
index e4c78a7f61..ad5c19138d 100644
--- a/apps/server/src/tests/config.spec.ts
+++ b/apps/server/tests/config.spec.ts
@@ -1,7 +1,7 @@
import { Test, TestingModule } from '@nestjs/testing';
import test from 'ava';
-import { Config, ConfigModule } from '../config';
+import { Config, ConfigModule } from '../src/config';
let config: Config;
let module: TestingModule;
diff --git a/apps/server/src/tests/doc.spec.ts b/apps/server/tests/doc.spec.ts
similarity index 93%
rename from apps/server/src/tests/doc.spec.ts
rename to apps/server/tests/doc.spec.ts
index a48875f0b1..53e8fc8bef 100644
--- a/apps/server/src/tests/doc.spec.ts
+++ b/apps/server/tests/doc.spec.ts
@@ -7,10 +7,10 @@ import { register } from 'prom-client';
import * as Sinon from 'sinon';
import { Doc as YDoc, encodeStateAsUpdate } from 'yjs';
-import { Config, ConfigModule } from '../config';
-import { MetricsModule } from '../metrics';
-import { DocManager, DocModule } from '../modules/doc';
-import { PrismaModule, PrismaService } from '../prisma';
+import { Config, ConfigModule } from '../src/config';
+import { MetricsModule } from '../src/metrics';
+import { DocManager, DocModule } from '../src/modules/doc';
+import { PrismaModule, PrismaService } from '../src/prisma';
import { flushDB } from './utils';
const createModule = () => {
diff --git a/apps/server/src/tests/exception-logger.e2e.ts b/apps/server/tests/exception-logger.e2e.ts
similarity index 90%
rename from apps/server/src/tests/exception-logger.e2e.ts
rename to apps/server/tests/exception-logger.e2e.ts
index ee798596e3..1180ae5c5d 100644
--- a/apps/server/src/tests/exception-logger.e2e.ts
+++ b/apps/server/tests/exception-logger.e2e.ts
@@ -1,13 +1,12 @@
import { Controller, Get, INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import test from 'ava';
-// @ts-expect-error graphql-upload is not typed
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import request from 'supertest';
-import { AppModule } from '../app';
-import { ExceptionLogger } from '../middleware/exception-logger';
-import { PrismaService } from '../prisma';
+import { AppModule } from '../src/app';
+import { ExceptionLogger } from '../src/middleware/exception-logger';
+import { PrismaService } from '../src/prisma';
const gql = '/graphql';
const rest = '/rest';
diff --git a/apps/server/src/tests/mailer.e2e.ts b/apps/server/tests/mailer.e2e.ts
similarity index 53%
rename from apps/server/src/tests/mailer.e2e.ts
rename to apps/server/tests/mailer.e2e.ts
index bdb1a501fe..6194b79691 100644
--- a/apps/server/src/tests/mailer.e2e.ts
+++ b/apps/server/tests/mailer.e2e.ts
@@ -1,4 +1,4 @@
-///
+///
// This test case is for testing the mailer service.
// Please use local SMTP server for testing.
// See: https://github.com/mailhog/MailHog
@@ -8,38 +8,21 @@ import {
} from '@affine-test/kit/utils/cloud';
import { Test, TestingModule } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
-import test from 'ava';
+import ava, { TestFn } from 'ava';
-import { ConfigModule } from '../config';
-import { GqlModule } from '../graphql.module';
-import { MetricsModule } from '../metrics';
-import { AuthModule } from '../modules/auth';
-import { AuthService } from '../modules/auth/service';
-import { PrismaModule } from '../prisma';
-import { RateLimiterModule } from '../throttler';
+import { ConfigModule } from '../src/config';
+import { GqlModule } from '../src/graphql.module';
+import { MetricsModule } from '../src/metrics';
+import { AuthModule } from '../src/modules/auth';
+import { AuthService } from '../src/modules/auth/service';
+import { PrismaModule } from '../src/prisma';
+import { RateLimiterModule } from '../src/throttler';
-let auth: AuthService;
-let module: TestingModule;
-let skip = false;
-
-test.before(async () => {
- try {
- const response = await fetch('http://localhost:8025/api/v2/messages');
- if (!response.ok && !process.env.CI) {
- console.warn('local mail not found, skip the mailer.e2e.ts');
- skip = true;
- }
- } catch (error) {
- if (
- error instanceof Error &&
- (error.cause as any)?.code === 'ECONNREFUSED' &&
- !process.env.CI
- ) {
- console.warn('local mail not found, skip the mailer.e2e.ts');
- skip = true;
- }
- }
-});
+const test = ava as TestFn<{
+ auth: AuthService;
+ module: TestingModule;
+ skip: boolean;
+}>;
// cleanup database before each test
test.beforeEach(async () => {
@@ -48,8 +31,8 @@ test.beforeEach(async () => {
await client.user.deleteMany({});
});
-test.beforeEach(async () => {
- module = await Test.createTestingModule({
+test.beforeEach(async t => {
+ t.context.module = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
auth: {
@@ -65,18 +48,16 @@ test.beforeEach(async () => {
RateLimiterModule,
],
}).compile();
- auth = module.get(AuthService);
+ t.context.auth = t.context.module.get(AuthService);
});
-test.afterEach.always(async () => {
- await module.close();
+test.afterEach.always(async t => {
+ await t.context.module.close();
});
test('should include callbackUrl in sending email', async t => {
- if (skip) {
- return t.pass();
- }
- // await auth.signUp('Alex Yang', 'alexyang@example.org', '123456');
+ const { auth } = t.context;
+ await auth.signUp('Alex Yang', 'alexyang@example.org', '123456');
for (const fn of [
'sendSetPasswordEmail',
'sendChangeEmail',
@@ -88,11 +69,10 @@ test('should include callbackUrl in sending email', async t => {
const current = await getCurrentMailMessageCount();
const mail = await getLatestMailMessage();
t.regex(
- mail.Content.Body,
+ mail?.Content?.Body,
/https:\/\/test.com\/callback/,
`should include callbackUrl when calling ${fn}`
);
t.is(current, prev + 1, `calling ${fn}`);
}
- return;
});
diff --git a/apps/server/src/tests/mailer.spec.ts b/apps/server/tests/mailer.spec.ts
similarity index 84%
rename from apps/server/src/tests/mailer.spec.ts
rename to apps/server/tests/mailer.spec.ts
index 19e82bbdb4..f85c6d9332 100644
--- a/apps/server/src/tests/mailer.spec.ts
+++ b/apps/server/tests/mailer.spec.ts
@@ -1,23 +1,17 @@
-import { ok } from 'node:assert';
import { randomUUID } from 'node:crypto';
import type { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { hashSync } from '@node-rs/argon2';
import { User } from '@prisma/client';
-import test from 'ava';
-// @ts-expect-error graphql-upload is not typed
+import ava, { TestFn } from 'ava';
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
-import { AppModule } from '../app';
-import { MailService } from '../modules/auth/mailer';
-import { PrismaService } from '../prisma';
+import { AppModule } from '../src/app';
+import { MailService } from '../src/modules/auth/mailer';
+import { PrismaService } from '../src/prisma';
import { createWorkspace, getInviteInfo, inviteUser, signUp } from './utils';
-let app: INestApplication;
-
-let mail: MailService;
-
const FakePrisma = {
fakeUser: {
id: randomUUID(),
@@ -95,14 +89,19 @@ const FakePrisma = {
},
};
-test.beforeEach(async () => {
+const test = ava as TestFn<{
+ app: INestApplication;
+ mail: MailService;
+}>;
+
+test.beforeEach(async t => {
const module = await Test.createTestingModule({
imports: [AppModule],
})
.overrideProvider(PrismaService)
.useValue(FakePrisma)
.compile();
- app = module.createNestApplication();
+ const app = module.createNestApplication();
app.use(
graphqlUploadExpress({
maxFileSize: 10 * 1024 * 1024,
@@ -111,14 +110,17 @@ test.beforeEach(async () => {
);
await app.init();
- mail = module.get(MailService);
+ const mail = module.get(MailService);
+ t.context.app = app;
+ t.context.mail = mail;
});
-test.afterEach.always(async () => {
- await app.close();
+test.afterEach.always(async t => {
+ await t.context.app.close();
});
test('should send invite email', async t => {
+ const { mail, app } = t.context;
if (mail.hasConfigured()) {
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
@@ -150,7 +152,7 @@ test('should send invite email', async t => {
}
);
- ok(resp.accepted.length === 1, 'failed to send invite email');
+ t.is(resp.accepted.length, 1, 'failed to send invite email');
}
t.pass();
});
diff --git a/apps/server/src/tests/prometheus-metrics.spec.ts b/apps/server/tests/prometheus-metrics.spec.ts
similarity index 93%
rename from apps/server/src/tests/prometheus-metrics.spec.ts
rename to apps/server/tests/prometheus-metrics.spec.ts
index a736d2fc43..c8fe384989 100644
--- a/apps/server/src/tests/prometheus-metrics.spec.ts
+++ b/apps/server/tests/prometheus-metrics.spec.ts
@@ -2,9 +2,9 @@ import { Test, TestingModule } from '@nestjs/testing';
import test from 'ava';
import { register } from 'prom-client';
-import { MetricsModule } from '../metrics';
-import { Metrics } from '../metrics/metrics';
-import { PrismaModule } from '../prisma';
+import { MetricsModule } from '../src/metrics';
+import { Metrics } from '../src/metrics/metrics';
+import { PrismaModule } from '../src/prisma';
let metrics: Metrics;
let module: TestingModule;
diff --git a/apps/server/src/tests/session.spec.ts b/apps/server/tests/session.spec.ts
similarity index 86%
rename from apps/server/src/tests/session.spec.ts
rename to apps/server/tests/session.spec.ts
index bdb9a135d2..a344452d9f 100644
--- a/apps/server/src/tests/session.spec.ts
+++ b/apps/server/tests/session.spec.ts
@@ -1,10 +1,10 @@
-///
+///
import { Test, TestingModule } from '@nestjs/testing';
import ava, { TestFn } from 'ava';
-import { ConfigModule } from '../config';
-import { SessionModule, SessionService } from '../session';
+import { ConfigModule } from '../src/config';
+import { SessionModule, SessionService } from '../src/session';
const test = ava as TestFn<{
session: SessionService;
diff --git a/apps/server/tests/tsconfig.json b/apps/server/tests/tsconfig.json
new file mode 100644
index 0000000000..353667c1aa
--- /dev/null
+++ b/apps/server/tests/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "target": "ESNext",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "rootDir": ".",
+ "outDir": "../dist/tests"
+ },
+ "references": [
+ {
+ "path": "../tsconfig.json"
+ },
+ {
+ "path": "../../../tests/kit/tsconfig.json"
+ }
+ ],
+ "include": ["."],
+ "exclude": []
+}
diff --git a/apps/server/src/tests/user.e2e.ts b/apps/server/tests/user.e2e.ts
similarity index 96%
rename from apps/server/src/tests/user.e2e.ts
rename to apps/server/tests/user.e2e.ts
index 403916991f..da06bc4322 100644
--- a/apps/server/src/tests/user.e2e.ts
+++ b/apps/server/tests/user.e2e.ts
@@ -2,11 +2,10 @@ import type { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import test from 'ava';
-// @ts-expect-error graphql-upload is not typed
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import request from 'supertest';
-import { AppModule } from '../app';
+import { AppModule } from '../src/app';
import { currentUser, signUp } from './utils';
let app: INestApplication;
diff --git a/apps/server/src/tests/utils.ts b/apps/server/tests/utils.ts
similarity index 98%
rename from apps/server/src/tests/utils.ts
rename to apps/server/tests/utils.ts
index 827c10f003..5f2f5abf9a 100644
--- a/apps/server/src/tests/utils.ts
+++ b/apps/server/tests/utils.ts
@@ -5,9 +5,9 @@ import { hashSync } from '@node-rs/argon2';
import { PrismaClient, User } from '@prisma/client';
import request from 'supertest';
-import type { TokenType } from '../modules/auth';
-import type { UserType } from '../modules/users';
-import type { InvitationType, WorkspaceType } from '../modules/workspaces';
+import type { TokenType } from '../src/modules/auth';
+import type { UserType } from '../src/modules/users';
+import type { InvitationType, WorkspaceType } from '../src/modules/workspaces';
const gql = '/graphql';
diff --git a/apps/server/src/tests/workspace-blobs.spec.ts b/apps/server/tests/workspace-blobs.spec.ts
similarity index 98%
rename from apps/server/src/tests/workspace-blobs.spec.ts
rename to apps/server/tests/workspace-blobs.spec.ts
index 617b2e9a28..acd53ebe34 100644
--- a/apps/server/src/tests/workspace-blobs.spec.ts
+++ b/apps/server/tests/workspace-blobs.spec.ts
@@ -2,11 +2,10 @@ import type { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import test from 'ava';
-// @ts-expect-error graphql-upload is not typed
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import request from 'supertest';
-import { AppModule } from '../app';
+import { AppModule } from '../src/app';
import {
checkBlobSize,
collectAllBlobSizes,
diff --git a/apps/server/src/tests/workspace-invite.e2e.ts b/apps/server/tests/workspace-invite.e2e.ts
similarity index 94%
rename from apps/server/src/tests/workspace-invite.e2e.ts
rename to apps/server/tests/workspace-invite.e2e.ts
index b38edce434..3dc21fb6de 100644
--- a/apps/server/src/tests/workspace-invite.e2e.ts
+++ b/apps/server/tests/workspace-invite.e2e.ts
@@ -6,12 +6,11 @@ import type { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import ava, { TestFn } from 'ava';
-// @ts-expect-error graphql-upload is not typed
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
-import { AppModule } from '../app';
-import { MailService } from '../modules/auth/mailer';
-import { AuthService } from '../modules/auth/service';
+import { AppModule } from '../src/app';
+import { MailService } from '../src/modules/auth/mailer';
+import { AuthService } from '../src/modules/auth/service';
import {
acceptInvite,
acceptInviteById,
@@ -202,8 +201,7 @@ test('should send email', async t => {
const inviteEmailContent = await getLatestMailMessage();
t.not(
- // @ts-expect-error Third part library type mismatch
- inviteEmailContent.To.find(item => {
+ inviteEmailContent.To.find((item: any) => {
return item.Mailbox === 'production';
}),
undefined,
@@ -221,8 +219,7 @@ test('should send email', async t => {
);
const acceptEmailContent = await getLatestMailMessage();
t.not(
- // @ts-expect-error Third part library type mismatch
- acceptEmailContent.To.find(item => {
+ acceptEmailContent.To.find((item: any) => {
return item.Mailbox === 'u1';
}),
undefined,
@@ -239,8 +236,7 @@ test('should send email', async t => {
);
const leaveEmailContent = await getLatestMailMessage();
t.not(
- // @ts-expect-error Third part library type mismatch
- leaveEmailContent.To.find(item => {
+ leaveEmailContent.To.find((item: any) => {
return item.Mailbox === 'u1';
}),
undefined,
diff --git a/apps/server/src/tests/workspace-usage.spec.ts b/apps/server/tests/workspace-usage.spec.ts
similarity index 83%
rename from apps/server/src/tests/workspace-usage.spec.ts
rename to apps/server/tests/workspace-usage.spec.ts
index 17a426bbee..9526db2f28 100644
--- a/apps/server/src/tests/workspace-usage.spec.ts
+++ b/apps/server/tests/workspace-usage.spec.ts
@@ -3,12 +3,12 @@ import { Test } from '@nestjs/testing';
import ava, { TestFn } from 'ava';
import { stub } from 'sinon';
-import { AppModule } from '../app';
-import { UsersService } from '../modules/users';
-import { PermissionService } from '../modules/workspaces/permission';
-import { WorkspaceResolver } from '../modules/workspaces/resolver';
-import { PrismaService } from '../prisma';
-import { StorageProvide } from '../storage';
+import { AppModule } from '../src/app';
+import { UsersService } from '../src/modules/users';
+import { PermissionService } from '../src/modules/workspaces/permission';
+import { WorkspaceResolver } from '../src/modules/workspaces/resolver';
+import { PrismaService } from '../src/prisma';
+import { StorageProvide } from '../src/storage';
import { FakePrisma } from './utils';
class FakePermission {
diff --git a/apps/server/src/tests/workspace.e2e.ts b/apps/server/tests/workspace.e2e.ts
similarity index 97%
rename from apps/server/src/tests/workspace.e2e.ts
rename to apps/server/tests/workspace.e2e.ts
index 83473528e0..1249d8dd8b 100644
--- a/apps/server/src/tests/workspace.e2e.ts
+++ b/apps/server/tests/workspace.e2e.ts
@@ -2,11 +2,10 @@ import type { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
import ava, { TestFn } from 'ava';
-// @ts-expect-error graphql-upload is not typed
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import request from 'supertest';
-import { AppModule } from '../app';
+import { AppModule } from '../src/app';
import {
acceptInvite,
createWorkspace,
@@ -133,7 +132,7 @@ test('should share a page', async t => {
t.is(pages.length, 1, 'failed to get shared pages');
t.is(pages[0], 'page1', 'failed to get shared page: page1');
- const msg1 = await sharePage(app, u2.token.token, workspace.id, 'page2');
+ const msg1 = await sharePage(app, u2.token.token, 'not_exists_ws', 'page2');
t.is(msg1, 'Permission denied', 'unauthorized user can share page');
const msg2 = await revokePage(app, u2.token.token, 'not_exists_ws', 'page2');
t.is(msg2, 'Permission denied', 'unauthorized user can share page');
diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json
index e41a2bba47..9cc86cac6d 100644
--- a/apps/server/tsconfig.json
+++ b/apps/server/tsconfig.json
@@ -16,8 +16,8 @@
"verbatimModuleSyntax": false,
"rootDir": "./src"
},
- "include": ["./src", "package.json"],
- "exclude": ["dist", "node_modules", "./src/tests"],
+ "include": ["src"],
+ "exclude": ["dist", "lib", "tests"],
"references": [
{
"path": "./tsconfig.node.json"
diff --git a/apps/server/tsconfig.node.json b/apps/server/tsconfig.node.json
index 15b75b25d8..82dc8124fe 100644
--- a/apps/server/tsconfig.node.json
+++ b/apps/server/tsconfig.node.json
@@ -18,5 +18,6 @@
"path": "../../tests/kit"
}
],
- "include": ["scripts", "package.json"]
+ "include": ["scripts", "package.json"],
+ "exclude": ["tests"]
}
diff --git a/packages/graphql/src/graphql/index.ts b/packages/graphql/src/graphql/index.ts
index 88758d93d7..d59ef1bba2 100644
--- a/packages/graphql/src/graphql/index.ts
+++ b/packages/graphql/src/graphql/index.ts
@@ -497,8 +497,8 @@ export const uploadAvatarMutation = {
definitionName: 'uploadAvatar',
containsFile: true,
query: `
-mutation uploadAvatar($id: String!, $avatar: Upload!) {
- uploadAvatar(id: $id, avatar: $avatar) {
+mutation uploadAvatar($avatar: Upload!) {
+ uploadAvatar(avatar: $avatar) {
id
name
avatarUrl
diff --git a/packages/graphql/src/graphql/upload-avatar.gql b/packages/graphql/src/graphql/upload-avatar.gql
index 372187677a..1e08c21e9d 100644
--- a/packages/graphql/src/graphql/upload-avatar.gql
+++ b/packages/graphql/src/graphql/upload-avatar.gql
@@ -1,5 +1,5 @@
-mutation uploadAvatar($id: String!, $avatar: Upload!) {
- uploadAvatar(id: $id, avatar: $avatar) {
+mutation uploadAvatar($avatar: Upload!) {
+ uploadAvatar(avatar: $avatar) {
id
name
avatarUrl
diff --git a/packages/graphql/src/schema.ts b/packages/graphql/src/schema.ts
index 8381dedbd9..20f0632227 100644
--- a/packages/graphql/src/schema.ts
+++ b/packages/graphql/src/schema.ts
@@ -441,7 +441,6 @@ export type SignUpMutation = {
};
export type UploadAvatarMutationVariables = Exact<{
- id: Scalars['String']['input'];
avatar: Scalars['Upload']['input'];
}>;
diff --git a/packages/i18n/tsconfig.node.json b/packages/i18n/tsconfig.node.json
index 344e213d32..fa411df147 100644
--- a/packages/i18n/tsconfig.node.json
+++ b/packages/i18n/tsconfig.node.json
@@ -1,7 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
- "types": ["node"]
+ "types": ["node"],
+ "outDir": "./lib/scripts"
},
"include": ["./src/scripts"]
}
diff --git a/packages/workspace/src/affine/__tests__/gql.spec.tsx b/packages/workspace/src/affine/__tests__/gql.spec.tsx
index 4bc16d9a1e..9bb659c1c8 100644
--- a/packages/workspace/src/affine/__tests__/gql.spec.tsx
+++ b/packages/workspace/src/affine/__tests__/gql.spec.tsx
@@ -136,7 +136,6 @@ describe('GraphQL wrapper for SWR', () => {
mutation: uploadAvatarMutation,
});
trigger({
- id: '1',
avatar: new File([''], 'avatar.png'),
});
}
diff --git a/tests/kit/package.json b/tests/kit/package.json
index 7ec0c6ffe6..43e563e8c6 100644
--- a/tests/kit/package.json
+++ b/tests/kit/package.json
@@ -1,6 +1,7 @@
{
"name": "@affine-test/kit",
"private": true,
+ "type": "module",
"version": "0.9.0-canary.12",
"exports": {
"./playwright": "./playwright.ts",
diff --git a/tsconfig.json b/tsconfig.json
index 1f1b9668ac..4bc570294e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -96,6 +96,9 @@
{
"path": "./apps/server"
},
+ {
+ "path": "./apps/server/tests"
+ },
{
"path": "./apps/storybook"
},
diff --git a/yarn.lock b/yarn.lock
index c70ea4b47a..04869cab66 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -690,6 +690,7 @@ __metadata:
"@types/cookie-parser": ^1.4.3
"@types/engine.io": ^3.1.7
"@types/express": ^4.17.17
+ "@types/graphql-upload": ^16.0.1
"@types/keyv": ^4.2.0
"@types/lodash-es": ^4.17.9
"@types/node": ^18.17.12
@@ -12695,6 +12696,15 @@ __metadata:
languageName: node
linkType: hard
+"@types/accepts@npm:*":
+ version: 1.3.5
+ resolution: "@types/accepts@npm:1.3.5"
+ dependencies:
+ "@types/node": "*"
+ checksum: 590b7580570534a640510c071e09074cf63b5958b237a728f94322567350aea4d239f8a9d897a12b15c856b992ee4d7907e9812bb079886af2c00714e7fb3f60
+ languageName: node
+ linkType: hard
+
"@types/affine__env@workspace:*, @types/affine__env@workspace:packages/@types/env":
version: 0.0.0-use.local
resolution: "@types/affine__env@workspace:packages/@types/env"
@@ -12841,6 +12851,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/content-disposition@npm:*":
+ version: 0.5.6
+ resolution: "@types/content-disposition@npm:0.5.6"
+ checksum: da07798d52cc8fc46a8843d768b48d54c70f1a44c861dc2c73c4c25a1e08af859709629ab0e4d23d5198107b8926bb48c593df436ba68123d87191f5e25fe4bc
+ languageName: node
+ linkType: hard
+
"@types/cookie-parser@npm:^1.4.3":
version: 1.4.3
resolution: "@types/cookie-parser@npm:1.4.3"
@@ -12864,6 +12881,18 @@ __metadata:
languageName: node
linkType: hard
+"@types/cookies@npm:*":
+ version: 0.7.8
+ resolution: "@types/cookies@npm:0.7.8"
+ dependencies:
+ "@types/connect": "*"
+ "@types/express": "*"
+ "@types/keygrip": "*"
+ "@types/node": "*"
+ checksum: 7945b0cfe370bf1f05a1f328c9eba55333dac1bb9d7efa3148b107c260ab924263546351f9fd168daa72948d195464d395319a24477995f9f887a3a99fbcb5b5
+ languageName: node
+ linkType: hard
+
"@types/cors@npm:^2.8.12":
version: 2.8.13
resolution: "@types/cors@npm:2.8.13"
@@ -13045,6 +13074,18 @@ __metadata:
languageName: node
linkType: hard
+"@types/graphql-upload@npm:^16.0.1":
+ version: 16.0.1
+ resolution: "@types/graphql-upload@npm:16.0.1"
+ dependencies:
+ "@types/express": "*"
+ "@types/koa": "*"
+ fs-capacitor: ^8.0.0
+ graphql: 0.13.1 - 16
+ checksum: 8ad2162f60f0808a6232cf2d2a8a7a10ce3e7a6a9cbb3bf80d8683a790b98b401d5ef69a83b8d011ce28ee67fdbb2ca1343051ea2133ec6a59772f94558967ba
+ languageName: node
+ linkType: hard
+
"@types/hast@npm:^2.0.0":
version: 2.3.5
resolution: "@types/hast@npm:2.3.5"
@@ -13061,6 +13102,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/http-assert@npm:*":
+ version: 1.5.3
+ resolution: "@types/http-assert@npm:1.5.3"
+ checksum: 9553e5a0b8bcfdac4b51d3fa3b89a91b5450171861a667a5b4c47204e0f4a1ca865d97396e6ceaf220e87b64d06b7a8bad7bfba15ef97acb41a87507c9940dbc
+ languageName: node
+ linkType: hard
+
"@types/http-cache-semantics@npm:*":
version: 4.0.1
resolution: "@types/http-cache-semantics@npm:4.0.1"
@@ -13172,6 +13220,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/keygrip@npm:*":
+ version: 1.0.3
+ resolution: "@types/keygrip@npm:1.0.3"
+ checksum: adee9a3efda3db9c64466af1c7c91a6d049420ee50589500cfd36e3e38d6abefdd858da88e6da63ed186e588127af3e862c1dc64fb0ad45c91870e6c35fe3be0
+ languageName: node
+ linkType: hard
+
"@types/keyv@npm:^3.1.4":
version: 3.1.4
resolution: "@types/keyv@npm:3.1.4"
@@ -13190,6 +13245,31 @@ __metadata:
languageName: node
linkType: hard
+"@types/koa-compose@npm:*":
+ version: 3.2.6
+ resolution: "@types/koa-compose@npm:3.2.6"
+ dependencies:
+ "@types/koa": "*"
+ checksum: 1204c5bfa4c69448b692aba29c566ef6bedbdbe5842fa180450267a23d3606faa13ef209876fd0c989edb5bc381812a66610fcfeac196ce4e76364354756ba1f
+ languageName: node
+ linkType: hard
+
+"@types/koa@npm:*":
+ version: 2.13.9
+ resolution: "@types/koa@npm:2.13.9"
+ dependencies:
+ "@types/accepts": "*"
+ "@types/content-disposition": "*"
+ "@types/cookies": "*"
+ "@types/http-assert": "*"
+ "@types/http-errors": "*"
+ "@types/keygrip": "*"
+ "@types/koa-compose": "*"
+ "@types/node": "*"
+ checksum: af9cd599c8e17e2ae0f4168a61d964e343f713d002b65fd995658d7addc6551ccadecfd32b3405cf44e4d360178ee4f972d6881533548261ae1f636a655d24b1
+ languageName: node
+ linkType: hard
+
"@types/lodash-es@npm:^4.17.6, @types/lodash-es@npm:^4.17.9":
version: 4.17.9
resolution: "@types/lodash-es@npm:4.17.9"
@@ -21956,7 +22036,7 @@ __metadata:
languageName: node
linkType: hard
-"graphql@npm:^15.0.0 || ^16.0.0, graphql@npm:^16.0.0, graphql@npm:^16.8.0":
+"graphql@npm:0.13.1 - 16, graphql@npm:^15.0.0 || ^16.0.0, graphql@npm:^16.0.0, graphql@npm:^16.8.0":
version: 16.8.0
resolution: "graphql@npm:16.8.0"
checksum: d853d4085b0c911a7e2a926c3b0d379934ec61cd4329e70cdf281763102f024fd80a97db7a505b8b04fed9050cb4875f8f518150ea854557a500a0b41dcd7f4e