diff --git a/.github/workflows/backend-test.yml b/.github/workflows/backend-test.yml index 4dc324f7c..235400d22 100644 --- a/.github/workflows/backend-test.yml +++ b/.github/workflows/backend-test.yml @@ -34,6 +34,6 @@ jobs: - name: backend npm install run: cd backend && npm install - name: set up prisma - run: cd backend && npx prisma db push --preview-feature && npx prisma generate + run: cd backend && npx prisma db push && npx prisma generate - name: backend test run: cd backend && timeout 300 make test \ No newline at end of file diff --git a/.github/workflows/storybook-test.yml b/.github/workflows/storybook-test.yml index f6fab372a..bed1c279f 100644 --- a/.github/workflows/storybook-test.yml +++ b/.github/workflows/storybook-test.yml @@ -1,11 +1,5 @@ # storybook test -# cache-playwright の key が `frontend/package-lock.json` になっているが -# playwright の version とかで 見た方がいいかもだが、 -# 現状 プルリクごとに playwright の install が走るので とりあえずこれで 問題なしとする。 -# 下記記事だと `package-lock.json` の 依存バージョン を 拾ってきているが この辺でもいいのか? -# - https://playwrightsolutions.com/playwright-github-action-to-cache-the-browser-binaries/ -# # ref # - https://github.com/microsoft/playwright-github-action # - `~/.cache/ms-playwright` @@ -20,10 +14,23 @@ on: pull_request: workflow_dispatch: +env: + DATABASE_URL: postgresql://test:test@localhost:5432/test?schema=public + jobs: storybook-test: - timeout-minutes: 5 + timeout-minutes: 10 runs-on: ubuntu-latest + services: + postgres: + image: postgres:latest + env: + POSTGRES_USER: test + POSTGRES_PASSWORD: test + POSTGRES_DB: test + ports: + - 5432:5432 + steps: - name: checkout git repository uses: actions/checkout@v3 @@ -35,18 +42,37 @@ jobs: path: frontend/node_modules key: cache-frontend-node-modules-${{ hashFiles('frontend/package-lock.json') }} + - name: playwright version + run: npx playwright -V > playwright-version.txt && cat playwright-version.txt + - name: cache playwright id: cache-playwright uses: actions/cache@v3 with: path: ~/.cache/ms-playwright - key: cache-playwright-${{ hashFiles('frontend/package-lock.json') }} + key: cache-playwright-${{ hashFiles('frontend/package-lock.json') }}-${{ hashFiles('playwright-version.txt') }} + + - name: cache backend node-modules + id: cache-backend-node-modules + uses: actions/cache@v3 + with: + path: backend/node_modules + key: cache-backend-node-modules-${{ hashFiles('backend/package-lock.json') }} + + - name: backend npm install + run: cd backend && npm install + - name: set up prisma + run: cd backend && npx prisma db push && npx prisma generate && npx prisma db seed + - name: run backend + run: cd backend && npm run start:dev & - name: frontend npm install run: cd frontend && npm install - if: ${{ steps.cache-playwright.outputs.cache-hit != 'true' }} name: install playwright - run: cd frontend && npx playwright install --with-deps + run: cd frontend && npx playwright install + - name: install playwright deps + run: cd frontend && npx playwright install-deps - name: is storybook file exists run: cd frontend && make sb-ck-file - name: run storybook test diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..31aa62b85 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.docker_build diff --git a/Makefile b/Makefile index be2f44272..6e998856d 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,27 @@ # MAKE # ------------------------------------------------------------------------------------------ +DOCKER_FILES := frontend/Dockerfile backend/Dockerfile db/Dockerfile +DOCKER_BUILD_TXT := .docker_build + PHONY := all -all: - docker compose up --build +all: $(DOCKER_BUILD_TXT) + $(MAKE) local-npm-i + $(MAKE) local-prisma-generate + docker compose up + +$(DOCKER_BUILD_TXT): $(DOCKER_FILES) + date > $(DOCKER_BUILD_TXT) + docker compose build + +PHONY += local-npm-i +local-npm-i: + cd frontend && npm i + cd backend && npm i + +PHONY += local-prisma-generate +local-prisma-generate: + cd backend && npx prisma generate PHONY += down down: @@ -46,11 +64,11 @@ lint-fix: PHONY += sb-test sb-test: - docker exec -t frontend make sb-test + docker compose run --rm frontend make sb-test PHONY += sb-update sb-update: - docker exec -t frontend make sb-update + docker compose run --rm frontend make sb-update # etc... # ------------------------------------------------------------------------------------------ diff --git a/backend/package-lock.json b/backend/package-lock.json index eed5fbcdd..f531eeea5 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -7899,9 +7899,9 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", - "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" diff --git a/backend/package.json b/backend/package.json index 508e3f869..67b5632b0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -7,7 +7,7 @@ "license": "UNLICENSED", "scripts": { "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" .", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" . && npx prisma format", "format-ck": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" .", "start": "nest start", "start:dev": "nest start --watch --preserveWatchOutput", diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index c436b958c..1ee36a181 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -9,71 +9,97 @@ datasource db { provider = "postgresql" url = env("DATABASE_URL") } + // userの情報 model User { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - username String @unique - email String @unique - hashedPassword String - messages Message[] - roomMembers RoomMember[] - userChatState UserChatState[] + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + username String @unique + email String @unique + hashedPassword String + messages Message[] + roomMembers RoomMember[] + userChatState UserChatState[] + friendship1 Friendship[] @relation("src") + friendship2 Friendship[] @relation("dest") } + // userが所属しているchatRoomの集合を取得 model ChatRoom { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - roomName String @unique + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + roomName String @unique password String? - isPrivate Boolean @default(false) - isDM Boolean @default(false) + isPrivate Boolean @default(false) + isDM Boolean @default(false) messages Message[] roomMembers RoomMember[] userChatState UserChatState[] } + // チャットルームの管理者からbanされた時にこのテーブルに追加、時間が経ったら削除 // ルームに入る際やチャットを送信するときに // このテーブルを都度チェックしてbanされてなかったらchatroomに入れる的な使い方 // endedAtに終了時間を渡す感じで使う予定 - model UserChatState { + chatRoomId String + userId String + userState UserChatStateCode + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + endedAt DateTime + user User @relation(fields: [userId], references: [id]) + chatRoom ChatRoom @relation(fields: [chatRoomId], references: [id], onDelete: Cascade) + @@id([chatRoomId, userId, userState]) - chatRoomId Int - userId Int - userState UserChatStateCode - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - endedAt DateTime - user User @relation(fields: [userId],references: [id]) - chatRoom ChatRoom @relation(fields: [chatRoomId],references: [id], onDelete: Cascade) } + // chatRoomに参加するときはこのテーブルに追加する // 抜けたときは削除する // UserMutedInChatRoom,UserBannedInChatRoomの情報をこのテーブルに持たせれば良くねって思ってるそこのあなた // このテーブルにban,muteの情報を持たせると、部屋から抜けた際に // その情報を持っているレコードが削除されてしまってもう一度入るみたいなことができてしまう model RoomMember { + joinedAt DateTime @default(now()) + role UserRole @default(USER) + userId String + chatRoomId String + user User @relation(fields: [userId], references: [id]) + chatRoom ChatRoom @relation(fields: [chatRoomId], references: [id], onDelete: Cascade) + @@id([userId, chatRoomId]) - joinedAt DateTime @default(now()) - role UserRole @default(USER) - userId Int - chatRoomId Int - user User @relation(fields: [userId],references: [id]) - chatRoom ChatRoom @relation(fields: [chatRoomId],references: [id], onDelete: Cascade) } + // chatRoomIdからそのchatRoomのmsg集合を取得したりする model Message { - id Int @id @default(autoincrement()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - content String - userId Int - chatRoomId Int - user User @relation(fields: [userId],references: [id]) - chatRoom ChatRoom @relation(fields: [chatRoomId],references: [id], onDelete: Cascade) + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + content String + userId String + chatRoomId String + user User @relation(fields: [userId], references: [id]) + chatRoom ChatRoom @relation(fields: [chatRoomId], references: [id], onDelete: Cascade) +} + +model Friendship { + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + srcUserId String + destUserId String + status FriendshipStatus + user1 User @relation("src", fields: [srcUserId], references: [id]) + user2 User @relation("dest", fields: [destUserId], references: [id]) + + @@id([srcUserId, destUserId]) +} + +enum FriendshipStatus { + Requested + Accepted + Blocked } enum UserRole { diff --git a/backend/prisma/seed.ts b/backend/prisma/seed.ts index 1b6d7b39f..3f253b8b5 100644 --- a/backend/prisma/seed.ts +++ b/backend/prisma/seed.ts @@ -37,28 +37,28 @@ async function main() { const roomMember1 = await prisma.roomMember.upsert({ where: { userId_chatRoomId: { - userId: 1, - chatRoomId: 1, + userId: user1.id, + chatRoomId: room.id, }, }, update: {}, create: { - userId: 1, - chatRoomId: 1, + userId: user1.id, + chatRoomId: room.id, role: UserRole.OWNER, }, }); const roomMember2 = await prisma.roomMember.upsert({ where: { userId_chatRoomId: { - userId: 2, - chatRoomId: 1, + userId: user2.id, + chatRoomId: room.id, }, }, update: {}, create: { - userId: 2, - chatRoomId: 1, + userId: user2.id, + chatRoomId: room.id, }, }); console.log(user1, user2); diff --git a/backend/src/events/events.gateway.ts b/backend/src/events/events.gateway.ts index 38d9f2eec..4d16f813c 100644 --- a/backend/src/events/events.gateway.ts +++ b/backend/src/events/events.gateway.ts @@ -36,12 +36,12 @@ export class EventsGateway { } @SubscribeMessage('getPastMessages') - async handleGetPastMessages(client: Socket, authorId: number) { + async handleGetPastMessages(client: Socket, authorId: string) { console.log('getPastMessages'); // Userテーブルのidでそのuserのmsgデータを全て取得 const pastMessages = await this.prisma.message.findMany({ where: { - userId: 1, + userId: authorId, }, }); console.log('mid', authorId); diff --git a/backend/src/post-message/dto/message.dto.ts b/backend/src/post-message/dto/message.dto.ts index 8481d7767..996a96a07 100644 --- a/backend/src/post-message/dto/message.dto.ts +++ b/backend/src/post-message/dto/message.dto.ts @@ -4,6 +4,6 @@ import { ApiProperty } from '@nestjs/swagger'; export class MessageDto { @ApiProperty({ example: 'test Message', description: 'test' }) content: string; - @ApiProperty({ example: 1, description: 'test' }) - authorId: number; + @ApiProperty({ example: 'id', description: 'test' }) + authorId: string; } diff --git a/backend/src/post-message/post-message.service.spec.ts b/backend/src/post-message/post-message.service.spec.ts index ca4dfa3ba..8dd3f35c3 100644 --- a/backend/src/post-message/post-message.service.spec.ts +++ b/backend/src/post-message/post-message.service.spec.ts @@ -5,18 +5,18 @@ import { PrismaService } from '../prisma/prisma.service'; import { PostMessageService } from './post-message.service'; type Message = { - id: number; + id: string; createdAt: Date; updatedAt: Date; content: string; - authorId: number; + authorId: string; }; const mockMsg: Message = { - id: 12, + id: '1', createdAt: new Date(), updatedAt: new Date(), content: 'nori', - authorId: 1, + authorId: '2', }; const mockPrismaService = { @@ -24,6 +24,9 @@ const mockPrismaService = { // PrismaService.createをmockMsgを返すだけの関数にした create: jest.fn().mockReturnValue(mockMsg), }, + chatRoom: { + findUnique: jest.fn().mockReturnValue({ id: '1' }), + }, }; describe('PostMessageService', () => { diff --git a/backend/src/post-message/post-message.service.ts b/backend/src/post-message/post-message.service.ts index 07f4bb8e2..642add362 100644 --- a/backend/src/post-message/post-message.service.ts +++ b/backend/src/post-message/post-message.service.ts @@ -10,11 +10,15 @@ export class PostMessageService { constructor(private prisma: PrismaService) {} async postMessage(dto: MessageDto): Promise { + // TODO 消す。roomIdをベタ打ちしてたけど、idがランダムになるから,部屋名で検索してidを取得する + const room = await this.prisma.chatRoom.findUnique({ + where: { roomName: 'hogeRoom' }, + }); const msg = await this.prisma.message.create({ data: { userId: dto.authorId, content: dto.content, - chatRoomId: 1, + chatRoomId: room?.id || '', }, }); return msg; diff --git a/backend/src/user/user.controller.ts b/backend/src/user/user.controller.ts index 4cde194fc..356811ccc 100644 --- a/backend/src/user/user.controller.ts +++ b/backend/src/user/user.controller.ts @@ -5,7 +5,7 @@ import { loginDto, signUpDto } from './dto/user.dto'; import { UserService } from './user.service'; // TODO front直したら消す type User = { - id: number; + id: string; createdAt: Date; updatedAt: Date; email: string; diff --git a/frontend/Makefile b/frontend/Makefile index 837cc3326..57c6b3a51 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -43,12 +43,12 @@ sb-create-file: PHONY += sb-test sb-test: - npm run storybook-test + export STORYBOOK_BACKEND="http://backend:8000" && npm run storybook-test-ci PHONY += sb-update sb-update: rm -rf $(shell find src/ -type d -name '__image_snapshots__') - npm run storybook-update-snapshot + export STORYBOOK_BACKEND="http://backend:8000" && npm run storybook-update-snapshot-ci # etc... # ------------------------------------------------------------------------------------------ diff --git a/frontend/doc/storybook.md b/frontend/doc/storybook.md index a7c07168b..114636c4b 100644 --- a/frontend/doc/storybook.md +++ b/frontend/doc/storybook.md @@ -17,6 +17,7 @@ pr : [88 storybook を 使って UI テスト](https://github.com/s-xix98/trc-pr ```shell npm install --save-dev @storybook/test-runner +npm install --save-dev @storybook/jest npm install --save-dev jest-image-snapshot ``` diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 07521d0e3..ccfccf1cd 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -32,6 +32,7 @@ "@storybook/addon-interactions": "^7.0.9", "@storybook/addon-links": "^7.0.9", "@storybook/blocks": "^7.0.9", + "@storybook/jest": "^0.1.0", "@storybook/nextjs": "^7.0.9", "@storybook/react": "^7.0.9", "@storybook/test-runner": "^0.10.0", @@ -51,6 +52,12 @@ "wait-on": "^7.0.1" } }, + "node_modules/@adobe/css-tools": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", + "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==", + "dev": true + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -5744,6 +5751,15 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/expect": { + "version": "27.5.2-0", + "resolved": "https://registry.npmjs.org/@storybook/expect/-/expect-27.5.2-0.tgz", + "integrity": "sha512-cP99mhWN/JeCp7VSIiymvj5tmuMY050iFohvp8Zq+kewKsBSZ6/qpTJAGCCZk6pneTcp4S0Fm5BSqyxzbyJ3gw==", + "dev": true, + "dependencies": { + "@types/jest": ">=26.0.0" + } + }, "node_modules/@storybook/global": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", @@ -5767,6 +5783,18 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/jest": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@storybook/jest/-/jest-0.1.0.tgz", + "integrity": "sha512-TmybnEXlv5Fu2/Hq4nRj7alS9mw4CasLR0RDwaAzS+Vpvu1TC4+j9rh+b1BHtmWebbJh0JMT6mgzPqOyJdgtQA==", + "dev": true, + "dependencies": { + "@storybook/expect": "storybook-jest", + "@storybook/instrumenter": "^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0", + "@testing-library/jest-dom": "^5.16.2", + "jest-mock": "^27.3.0" + } + }, "node_modules/@storybook/manager": { "version": "7.0.9", "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-7.0.9.tgz", @@ -6391,6 +6419,41 @@ "node": ">=12" } }, + "node_modules/@testing-library/jest-dom": { + "version": "5.16.5", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", + "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@testing-library/user-event": { "version": "13.5.0", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", @@ -6602,6 +6665,144 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.1.tgz", + "integrity": "sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jest/node_modules/@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-diff": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-matcher-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@types/jest/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -6808,6 +7009,15 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.6", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz", + "integrity": "sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA==", + "dev": true, + "dependencies": { + "@types/jest": "*" + } + }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -9276,6 +9486,12 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -18534,6 +18750,19 @@ "node": ">= 0.10" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -19344,9 +19573,9 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", - "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" diff --git a/frontend/package.json b/frontend/package.json index 715daf0f7..2499fb0b2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,9 +13,10 @@ "lint-fix": "eslint --fix src", "noEmit": "tsc --noEmit", "storybook": "storybook dev -p 6006 --quiet", - "storybook-test": "test-storybook", + "storybook-test": "test-storybook --maxWorkers=1", "storybook-test-ci": "concurrently --kill-others --success first \"npm run storybook\" \"wait-on tcp:6006 && npm run storybook-test\"", "storybook-update-snapshot": "test-storybook --updateSnapshot", + "storybook-update-snapshot-ci": "concurrently --kill-others --success first \"npm run storybook\" \"wait-on tcp:6006 && npm run storybook-update-snapshot\"", "build-storybook": "storybook build", "concurrent": "concurrently \"npm run dev\" \"npm run storybook\"" }, @@ -44,6 +45,7 @@ "@storybook/addon-interactions": "^7.0.9", "@storybook/addon-links": "^7.0.9", "@storybook/blocks": "^7.0.9", + "@storybook/jest": "^0.1.0", "@storybook/nextjs": "^7.0.9", "@storybook/react": "^7.0.9", "@storybook/test-runner": "^0.10.0", diff --git a/frontend/scripts/install_dev_dependencies.sh b/frontend/scripts/install_dev_dependencies.sh index 0d383eed7..2073d4c9c 100644 --- a/frontend/scripts/install_dev_dependencies.sh +++ b/frontend/scripts/install_dev_dependencies.sh @@ -6,6 +6,7 @@ npm install --save-dev concurrently # storybook # npx storybook@latest init npm install --save-dev @storybook/test-runner +npm install --save-dev @storybook/jest npm install --save-dev jest-image-snapshot # wait-on diff --git a/frontend/src/app/__image_snapshots__/app-page--login.png b/frontend/src/app/__image_snapshots__/app-page--login.png new file mode 100644 index 000000000..e8b565e26 Binary files /dev/null and b/frontend/src/app/__image_snapshots__/app-page--login.png differ diff --git a/frontend/src/app/__image_snapshots__/app-page--select-channel.png b/frontend/src/app/__image_snapshots__/app-page--select-channel.png new file mode 100644 index 000000000..10c8db507 Binary files /dev/null and b/frontend/src/app/__image_snapshots__/app-page--select-channel.png differ diff --git a/frontend/src/app/__image_snapshots__/app-page--send-msg.png b/frontend/src/app/__image_snapshots__/app-page--send-msg.png new file mode 100644 index 000000000..7199b0258 Binary files /dev/null and b/frontend/src/app/__image_snapshots__/app-page--send-msg.png differ diff --git a/frontend/src/app/__image_snapshots__/app-page--send-some-msg.png b/frontend/src/app/__image_snapshots__/app-page--send-some-msg.png new file mode 100644 index 000000000..ceed75670 Binary files /dev/null and b/frontend/src/app/__image_snapshots__/app-page--send-some-msg.png differ diff --git a/frontend/src/app/page.stories.tsx b/frontend/src/app/page.stories.tsx index 59869c1f7..58dec85ee 100644 --- a/frontend/src/app/page.stories.tsx +++ b/frontend/src/app/page.stories.tsx @@ -1,7 +1,11 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { userEvent, within } from '@storybook/testing-library'; +import { expect } from '@storybook/jest'; import Home from './page'; +const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms)); + const meta = { component: Home, tags: ['autodocs'], @@ -12,3 +16,75 @@ export default meta; type Story = StoryObj; export const Basic: Story = {}; + +export const Login: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const loginBtn = canvas.getByText('login as fuga'); + await userEvent.click(loginBtn); + await sleep(1000); + expect(canvas.getByText('ChatChannelArea')); + }, +}; + +export const SelectChannel: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const hoge0 = canvas.getByText('hoge 0'); + await userEvent.click(hoge0); + + await sleep(1000); + }, +}; + +export const SendMsg: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const hoge0 = canvas.getByText('hoge 0'); + await userEvent.click(hoge0); + + const inputElem = + canvas.getByTestId('input-test-id')?.firstElementChild?.firstElementChild; + if (inputElem === undefined || inputElem === null) { + expect(false); + return; + } + await userEvent.type(inputElem, 'This is test msg'); + await userEvent.click(canvas.getByText('Send')); + + // SEND ボタンに アニメーションがあり、スクショのタイミングによって + // スクショに若干の差異が生まれ テストが落ちてしまうので 適当に Footer を クリック + await userEvent.click(canvas.getByText('Footer')); + + await sleep(1000); + }, +}; + +export const SendSomeMsg: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const hoge0 = canvas.getByText('hoge 0'); + await userEvent.click(hoge0); + + const inputElem = + canvas.getByTestId('input-test-id')?.firstElementChild?.firstElementChild; + if (inputElem === undefined || inputElem === null) { + expect(false); + return; + } + for (let i = 0; i < 30; i++) { + await userEvent.type(inputElem, `This is test msg ${i}`); + await userEvent.click(canvas.getByText('Send')); + } + + // SEND ボタンに アニメーションがあり、スクショのタイミングによって + // スクショに若干の差異が生まれ テストが落ちてしまうので 適当に Footer を クリック + await userEvent.click(canvas.getByText('Footer')); + + await sleep(1000); + }, +}; diff --git a/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--act.png b/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--act.png index b13c73f07..95b1dc556 100644 Binary files a/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--act.png and b/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--act.png differ diff --git a/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--basic.png b/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--basic.png index b13c73f07..95b1dc556 100644 Binary files a/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--basic.png and b/frontend/src/components/Elements/Button/__image_snapshots__/components-elements-button-sendbutton--basic.png differ diff --git a/frontend/src/components/Elements/Input/Input.tsx b/frontend/src/components/Elements/Input/Input.tsx index c4ded68a8..9ebbe2459 100644 --- a/frontend/src/components/Elements/Input/Input.tsx +++ b/frontend/src/components/Elements/Input/Input.tsx @@ -12,6 +12,7 @@ export const Input = ({ <> { - const url = 'http://localhost:8000/post-message'; +import { BACKEND } from '../../../constants'; + +export const postMessage = (authorId: string, msg: string) => { + const url = BACKEND + '/post-message'; const msgDto: MessageDto = { content: msg, authorId: authorId }; diff --git a/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chatinput--basic.png b/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chatinput--basic.png index 20b33ed79..3518ee055 100644 Binary files a/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chatinput--basic.png and b/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chatinput--basic.png differ diff --git a/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chattalkarea--basic.png b/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chattalkarea--basic.png index 066d76e4e..7922001da 100644 Binary files a/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chattalkarea--basic.png and b/frontend/src/features/chat/components/__image_snapshots__/features-chat-components-chattalkarea--basic.png differ diff --git a/frontend/src/features/chat/types/MessageDto.ts b/frontend/src/features/chat/types/MessageDto.ts index 90a2e244c..fa25525f7 100644 --- a/frontend/src/features/chat/types/MessageDto.ts +++ b/frontend/src/features/chat/types/MessageDto.ts @@ -2,7 +2,7 @@ export type MessageDto = { content: string; - authorId: number; + authorId: string; }; export type handleMessageDto = { diff --git a/frontend/src/features/user/api/userLogin.ts b/frontend/src/features/user/api/userLogin.ts index 34ee8445b..b41deb917 100644 --- a/frontend/src/features/user/api/userLogin.ts +++ b/frontend/src/features/user/api/userLogin.ts @@ -2,13 +2,15 @@ import axios, { AxiosResponse } from 'axios'; import { LoginDto, UserInfo } from '../types/UserDto'; +import { BACKEND } from '../../../constants'; + export const userLogin = async ( email: string, passwd: string, setUserInfo: React.Dispatch>, ) => { console.log('post'); - const url = 'http://localhost:8000/user/login'; + const url = BACKEND + '/user/login'; const loginDto: LoginDto = { email: email, hashedPassword: passwd }; // postでブロックしても問題ないならasync awaitでもいいかも diff --git a/frontend/src/features/user/api/userSignUp.ts b/frontend/src/features/user/api/userSignUp.ts index 6ba403992..559f538d0 100644 --- a/frontend/src/features/user/api/userSignUp.ts +++ b/frontend/src/features/user/api/userSignUp.ts @@ -2,6 +2,8 @@ import axios, { AxiosResponse } from 'axios'; import { SignUpDto, UserInfo } from '../types/UserDto'; +import { BACKEND } from '../../../constants'; + export const userSignUp = async ( nickname: string, email: string, @@ -9,7 +11,7 @@ export const userSignUp = async ( setUserInfo: (v: UserInfo) => void, ) => { console.log('post /user/signup'); - const url = 'http://localhost:8000/user/signup'; + const url = BACKEND + '/user/signup'; // TODO validation フロントとバックどっちの責任? const signUpDto: SignUpDto = { diff --git a/frontend/src/features/user/components/User.tsx b/frontend/src/features/user/components/User.tsx index cf77d274d..4fba4e50e 100644 --- a/frontend/src/features/user/components/User.tsx +++ b/frontend/src/features/user/components/User.tsx @@ -28,9 +28,7 @@ export const User = ({ children }: { children: ReactNode }) => { > botton -

- id : {userInfo?.id}, name : {userInfo?.nickname} -

+

name : {userInfo?.nickname}