diff --git a/next-ui/src/mocks/api/handlers.ts b/next-ui/src/mocks/api/handlers.ts index 6292f56e..836750ad 100644 --- a/next-ui/src/mocks/api/handlers.ts +++ b/next-ui/src/mocks/api/handlers.ts @@ -6,15 +6,17 @@ import { librariesHandlers } from '@/mocks/api/handlers/libraries' import { referentialHandlers } from '@/mocks/api/handlers/referential' import { usersHandlers } from '@/mocks/api/handlers/users' import { settingsHandlers } from '@/mocks/api/handlers/settings' +import { claimHandlers } from '@/mocks/api/handlers/claim' export const handlers = [ - ...librariesHandlers, - ...referentialHandlers, ...actuatorHandlers, ...announcementHandlers, + ...claimHandlers, + ...librariesHandlers, + ...referentialHandlers, ...releasesHandlers, - ...usersHandlers, ...settingsHandlers, + ...usersHandlers, ] export const response401Unauthorized = () => diff --git a/next-ui/src/mocks/api/handlers/claim.ts b/next-ui/src/mocks/api/handlers/claim.ts new file mode 100644 index 00000000..9a82b188 --- /dev/null +++ b/next-ui/src/mocks/api/handlers/claim.ts @@ -0,0 +1,5 @@ +import { httpTyped } from '@/mocks/api/httpTyped' + +export const claimHandlers = [ + httpTyped.get('/api/v1/claim', ({ response }) => response(200).json({ isClaimed: true })), +] diff --git a/next-ui/src/pages/claim.mdx b/next-ui/src/pages/claim.mdx new file mode 100644 index 00000000..281f4d2a --- /dev/null +++ b/next-ui/src/pages/claim.mdx @@ -0,0 +1,15 @@ +import { Canvas, Meta } from '@storybook/addon-docs/blocks'; + +import * as Stories from './claim.stories'; + + + +# Claim + +If the server is unclaimed, this page will be used instead of the login page. This is how they differ: +- information banner displayed at the top +- password confirmation is required +- remember me is not shown +- OAuth2 login is not shown + + diff --git a/next-ui/src/pages/claim.stories.ts b/next-ui/src/pages/claim.stories.ts new file mode 100644 index 00000000..d6f51b58 --- /dev/null +++ b/next-ui/src/pages/claim.stories.ts @@ -0,0 +1,112 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite' + +import claim from './claim.vue' +import { http, delay } from 'msw' + +import { response502BadGateway } from '@/mocks/api/handlers' +import { expect, waitFor } from 'storybook/test' +import { useMessagesStore } from '@/stores/messages' +import { httpTyped } from '@/mocks/api/httpTyped' + +const meta = { + component: claim, + render: (args: object) => ({ + components: { claim }, + setup() { + return { args } + }, + template: '', + }), + parameters: { + // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout + msw: { + handlers: [ + httpTyped.get('/api/v1/claim', ({ response }) => response(200).json({ isClaimed: false })), + ], + }, + }, + args: {}, +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: {}, +} + +export const Invalid: Story = { + play: async ({ canvas, userEvent }) => { + const login = canvas.getByLabelText(/email/i, { + selector: 'input', + }) + await userEvent.type(login, 'test@example.org') + + const password1 = canvas.getByLabelText(/^password/i, { + selector: 'input', + }) + await userEvent.type(password1, 'abc') + + const password2 = canvas.getByLabelText(/confirm password/i, { + selector: 'input', + }) + await userEvent.type(password2, 'def') + + await userEvent.click(canvas.getByRole('button', { name: /create/i })) + }, +} + +export const Loading: Story = { + parameters: { + msw: { + handlers: [http.all('*', async () => await delay(5_000))], + }, + }, + play: async ({ canvas, userEvent }) => { + const login = canvas.getByLabelText(/email/i, { + selector: 'input', + }) + await userEvent.type(login, 'test@example.org') + + const password1 = canvas.getByLabelText(/^password/i, { + selector: 'input', + }) + await userEvent.type(password1, 'abc') + + const password2 = canvas.getByLabelText(/confirm password/i, { + selector: 'input', + }) + await userEvent.type(password2, 'abc') + + await userEvent.click(canvas.getByRole('button', { name: /create/i })) + }, +} + +export const Error: Story = { + parameters: { + msw: { + handlers: [http.all('*', response502BadGateway)], + }, + }, + play: async ({ canvas, userEvent }) => { + const login = canvas.getByLabelText(/email/i, { + selector: 'input', + }) + await userEvent.type(login, 'test@example.org') + + const password1 = canvas.getByLabelText(/^password/i, { + selector: 'input', + }) + await userEvent.type(password1, 'abc') + + const password2 = canvas.getByLabelText(/confirm password/i, { + selector: 'input', + }) + await userEvent.type(password2, 'abc') + + await userEvent.click(canvas.getByRole('button', { name: /create/i })) + + const messagesStore = useMessagesStore() + await waitFor(() => expect(messagesStore.messages.length).toBe(1)) + }, +}