From e503468660a13760010a94ecda5f0625c6f47f87 Mon Sep 17 00:00:00 2001 From: Ricardo Date: Fri, 13 Oct 2023 14:12:03 +0200 Subject: Server re-build with latest AdonisJS framework & Typescript (#47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: setup first basis structure * chore: ensure styling is loaded correctly * chore: comply to new routing syntax by replace . with / in routes/resource locations * chore: add login controller * chore: correctly use views with slash instead of dot * chore: working login + tests * chore: clean up tests * chore: add password-forgot endpoint and matching test * chore: add delete page test * chore: add logout test * chore: add reset-password route and tests * chore: remove obsolete comment * chore: add account-page and tests * chore: add data page & first step of the test * chore: add transfer/import data feature and tests * chore: add export and basic test * chore: add all static api routes with tests * Regenerate 'pnpm-lock.json' and fix bad merge conflict WIP: - Tests have been commented out since they dont work - Server doesn't start * easier dev and test runs * - remove --require-pragma from reformat-files so formatting works properly - run pnpm reformat-files over codebase - remove .json files from .eslintignore - add invalid.json file to .eslintignore - configure prettier properly in eslint config - add type jsdoc to prettier config - run adonis generate:manifest command to regenerate ace-manifest.json - specify volta in package.json - introduce typecheck npm script - remove unused .mjs extension from npm scripts - install missing type definition dependencies - add pnpm.allowedDeprecatedVersions to package.json - fix invalid extends in tsconfig.json causing TS issues throughout codebase - remove @ts-ignore throughout codebase which is not relevant anymore - enable some of the tsconfig options - remove outdated eslint-disable from codebase - change deprecated faker.company.companyName() to faker.company.name() - fix TS issues inside transfer.spec.ts * - update to latest node and pnpm versions - upgrade all non-major dependencies to latest - install missing @types/luxon dependency - add cuid to pnpm.allowedDeprecatedVersions - add esModuleInterop config option to tsconfig - migrate more deprecated faker methods to new ones - add more temporary ts-ignore to code * - update eslint config - remove trailingComma: all since default in prettier v3 - add typecheck command to prepare-code npm script - upgrade various dependencies to latest major version - update tsconfig to include only useful config options - disable some lint issues and fix others * - add test command to prepare-code - disable strictPropertyInitialization flag in tsconfig which creates issues with adonis models - update precommit hook to excute pnpm prepare-code - remove ts-ignore statements from all models * fix node and pnpm dependency update * add cross env (so that we can develop on windows) * add signup endpoint (TODO: JWT auth) * Add login endpoint * Add me and updateMe endpoints * Add service endpoint * refactor: change endpoints to use jwt * add recipes endpoint * add workspaces endpoint * fix web controllors for login and post import * Update node deps * Change auth middleware (for web) and exempt api from CSRF * Add import endpoint (franz import) * Fix export/import logic * Fix service and workspace data in user/data * Fix partial lint * chore: workaround lint issues * fix: migration naming had two . * Sync back node with recipes repo * Temporarily ignore typescript * Fix adonisrc to handle public folder static assets * Fix issue with production database * add Legacy Password Provider * Fix lint errors * Fix issue on login errors frontend * add Legacy Password Provider * Fix issue with customIcons * Fix issue with auth tokens * Update 'node' to '18.18.0' * make docker work * improve docker entrypoint (test api performance) * Add migration database script * NODE_ENV on recipes * prefer @ts-expect-error over @ts-ignore * small fixes * Update 'pnpm' to '8.7.6' * fix error catch * Automatically generate JWT Public and Private keys * Use custom Adonis5-jwt * Update code to use secret (old way, no breaking changes) * Normalize appKey * Trick to make JWT tokens on client work with new version * Fix error with new JWT logic * Change migration and how we store JWT * Fix 500 response code (needs to be 401) * Improve logic and fix bugs * Fix build and entrypoint logic * Catch error if appKey changes * Add newToken logic * Fix lint (ignore any errors) * Add build for PRs * pnpm reformat-files result * Fix some tests * Fix reset password not working (test failing) * Restore csrfTokens (disabled by accident) * Fix pnpm start command with .env * Disable failing tests on the transfer endpoint (TODO) * Add tests to PR build * Fix build * Remove unnecessary assertStatus * Add typecheck * hash password on UserFactory (fix build) * Add JWT_USE_PEM true by default (increase security) * fix name of github action --------- Co-authored-by: Vijay A Co-authored-by: Balaji Vijayakumar Co-authored-by: MCMXC <16797721+mcmxcdev@users.noreply.github.com> Co-authored-by: André Oliveira --- tests/functional/api/register.spec.ts | 0 tests/functional/api/static/announcements.spec.ts | 55 +++++ tests/functional/api/static/features.spec.ts | 29 +++ tests/functional/api/static/news.spec.ts | 11 + tests/functional/api/static/services.spec.ts | 11 + tests/functional/dashboard/account.spec.ts | 124 ++++++++++++ tests/functional/dashboard/data.spec.ts | 31 +++ tests/functional/dashboard/delete.spec.ts | 37 ++++ .../dashboard/disabled-dashboard.spec.ts | 71 +++++++ tests/functional/dashboard/export.spec.ts | 61 ++++++ tests/functional/dashboard/forgot-password.spec.ts | 70 +++++++ .../functional/dashboard/import-stubs/invalid.json | 1 + .../dashboard/import-stubs/random-file.txt | 0 .../import-stubs/services-only.ferdi-data | 38 ++++ .../import-stubs/services-only.ferdium-data | 38 ++++ .../dashboard/import-stubs/services-only.json | 38 ++++ .../import-stubs/services-workspaces.ferdi-data | 61 ++++++ .../import-stubs/services-workspaces.ferdium-data | 61 ++++++ .../import-stubs/services-workspaces.json | 64 ++++++ .../dashboard/import-stubs/valid-no-data.json | 3 + .../import-stubs/workspaces-only.ferdi-data | 26 +++ .../import-stubs/workspaces-only.ferdium-data | 26 +++ .../dashboard/import-stubs/workspaces-only.json | 26 +++ tests/functional/dashboard/login.spec.ts | 65 ++++++ tests/functional/dashboard/logout.spec.ts | 21 ++ tests/functional/dashboard/reset-password.spec.ts | 122 +++++++++++ tests/functional/dashboard/transfer.spec.ts | 222 +++++++++++++++++++++ tests/functional/health.spec.ts | 13 ++ tests/functional/static-pages/home.spec.ts | 10 + tests/functional/static-pages/privacy.spec.ts | 10 + tests/functional/static-pages/terms.spec.ts | 10 + 31 files changed, 1355 insertions(+) create mode 100644 tests/functional/api/register.spec.ts create mode 100644 tests/functional/api/static/announcements.spec.ts create mode 100644 tests/functional/api/static/features.spec.ts create mode 100644 tests/functional/api/static/news.spec.ts create mode 100644 tests/functional/api/static/services.spec.ts create mode 100644 tests/functional/dashboard/account.spec.ts create mode 100644 tests/functional/dashboard/data.spec.ts create mode 100644 tests/functional/dashboard/delete.spec.ts create mode 100644 tests/functional/dashboard/disabled-dashboard.spec.ts create mode 100644 tests/functional/dashboard/export.spec.ts create mode 100644 tests/functional/dashboard/forgot-password.spec.ts create mode 100644 tests/functional/dashboard/import-stubs/invalid.json create mode 100644 tests/functional/dashboard/import-stubs/random-file.txt create mode 100644 tests/functional/dashboard/import-stubs/services-only.ferdi-data create mode 100644 tests/functional/dashboard/import-stubs/services-only.ferdium-data create mode 100644 tests/functional/dashboard/import-stubs/services-only.json create mode 100644 tests/functional/dashboard/import-stubs/services-workspaces.ferdi-data create mode 100644 tests/functional/dashboard/import-stubs/services-workspaces.ferdium-data create mode 100644 tests/functional/dashboard/import-stubs/services-workspaces.json create mode 100644 tests/functional/dashboard/import-stubs/valid-no-data.json create mode 100644 tests/functional/dashboard/import-stubs/workspaces-only.ferdi-data create mode 100644 tests/functional/dashboard/import-stubs/workspaces-only.ferdium-data create mode 100644 tests/functional/dashboard/import-stubs/workspaces-only.json create mode 100644 tests/functional/dashboard/login.spec.ts create mode 100644 tests/functional/dashboard/logout.spec.ts create mode 100644 tests/functional/dashboard/reset-password.spec.ts create mode 100644 tests/functional/dashboard/transfer.spec.ts create mode 100644 tests/functional/health.spec.ts create mode 100644 tests/functional/static-pages/home.spec.ts create mode 100644 tests/functional/static-pages/privacy.spec.ts create mode 100644 tests/functional/static-pages/terms.spec.ts (limited to 'tests/functional') diff --git a/tests/functional/api/register.spec.ts b/tests/functional/api/register.spec.ts new file mode 100644 index 0000000..e69de29 diff --git a/tests/functional/api/static/announcements.spec.ts b/tests/functional/api/static/announcements.spec.ts new file mode 100644 index 0000000..ac933fe --- /dev/null +++ b/tests/functional/api/static/announcements.spec.ts @@ -0,0 +1,55 @@ +import { test } from '@japa/runner'; +import { apiVersion } from '../../../config'; + +test.group('API / Static / News', () => { + test('returns a 404 response when the requested versions does not exist', async ({ + client, + }) => { + const response = await client.get(`/${apiVersion}/announcements/illegal`); + + response.assertStatus(404); + response.assertTextIncludes('No announcement found.'); + }); + + test('returns a 200 response with default version file when specifying version as input', async ({ + client, + }) => { + const response = await client.get(`/${apiVersion}/announcements/version`); + + response.assertStatus(200); + response.assertBody({ + main: { + headline: 'Example Announcement', + subHeadline: 'Configure your announcement here', + image: { + light: 'https://api.ferdium.org/assets/feature/light.png', + dark: 'https://api.ferdium.org/assets/feature/dark.png', + }, + text: 'Long description here', + cta: { + label: 'Click here to do something', + href: '/settings/app', + analytics: { + category: 'announcements-main', + action: 'event', + label: 'This does not get used', + }, + }, + }, + spotlight: { + title: 'Spotlight:', + subject: 'Show another feature', + text: 'Show another feature in the spotlight', + cta: { + label: 'Click here to do something', + href: '/settings/team', + analytics: { + category: 'announcements-spotlight', + action: 'event', + label: 'This does not get used', + }, + }, + }, + }); + }); +}); diff --git a/tests/functional/api/static/features.spec.ts b/tests/functional/api/static/features.spec.ts new file mode 100644 index 0000000..1050fcf --- /dev/null +++ b/tests/functional/api/static/features.spec.ts @@ -0,0 +1,29 @@ +import { test } from '@japa/runner'; +import { apiVersion } from '../../../config'; + +const defaultResponse = { + isServiceProxyEnabled: true, + isWorkspaceEnabled: true, + isAnnouncementsEnabled: true, + isSettingsWSEnabled: false, + isMagicBarEnabled: true, + isTodosEnabled: true, +}; + +test.group('API / Static / Features', () => { + test('returns a 200 response with empty array', async ({ client }) => { + const response = await client.get(`/${apiVersion}/features`); + + response.assertStatus(200); + response.assertBody(defaultResponse); + }); + + test('returns a 200 response with expected object when calling with :mode', async ({ + client, + }) => { + const response = await client.get(`/${apiVersion}/features/random`); + + response.assertStatus(200); + response.assertBody(defaultResponse); + }); +}); diff --git a/tests/functional/api/static/news.spec.ts b/tests/functional/api/static/news.spec.ts new file mode 100644 index 0000000..8f3818f --- /dev/null +++ b/tests/functional/api/static/news.spec.ts @@ -0,0 +1,11 @@ +import { test } from '@japa/runner'; +import { apiVersion } from '../../../config'; + +test.group('API / Static / News', () => { + test('returns a 200 response with empty array', async ({ client }) => { + const response = await client.get(`/${apiVersion}/news`); + + response.assertStatus(200); + response.assertBody([]); + }); +}); diff --git a/tests/functional/api/static/services.spec.ts b/tests/functional/api/static/services.spec.ts new file mode 100644 index 0000000..55eef37 --- /dev/null +++ b/tests/functional/api/static/services.spec.ts @@ -0,0 +1,11 @@ +import { test } from '@japa/runner'; +import { apiVersion } from '../../../config'; + +test.group('API / Static / Services', () => { + test('returns a 200 response with empty array', async ({ client }) => { + const response = await client.get(`/${apiVersion}/services`); + + response.assertStatus(200); + response.assertBody([]); + }); +}); diff --git a/tests/functional/dashboard/account.spec.ts b/tests/functional/dashboard/account.spec.ts new file mode 100644 index 0000000..bee9d6a --- /dev/null +++ b/tests/functional/dashboard/account.spec.ts @@ -0,0 +1,124 @@ +import { test } from '@japa/runner'; +import User from 'App/Models/User'; +import UserFactory from 'Database/factories/UserFactory'; + +test.group('Dashboard / Account page', () => { + test('redirects to /user/login when accessing /user/account as guest', async ({ + client, + }) => { + const response = await client.get('/user/account'); + + response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL + }); + + test('returns a 200 opening the account route while logged in', async ({ + client, + }) => { + const user = await UserFactory.create(); + const response = await client.get('/user/account').loginAs(user); + + response.assertStatus(200); + response.assertTextIncludes('Your Ferdium account'); + + response.assertTextIncludes(user.email); + response.assertTextIncludes(user.username); + response.assertTextIncludes(user.lastname); + }); + + test('returns a validation error for all fields when missing', async ({ + client, + }) => { + const user = await UserFactory.create(); + const response = await client.post('/user/account').loginAs(user); + + response.assertTextIncludes( + 'value="required validation failed,required validation failed" placeholder="E-Mail"', + ); + response.assertTextIncludes( + 'value="required validation failed,required validation failed" placeholder="Name"', + ); + response.assertTextIncludes( + 'value="required validation failed,required validation failed" placeholder="Last Name"', + ); + }); + + test('returns a validation error for username when there is another user with same username', async ({ + client, + }) => { + const user = await UserFactory.create(); + const existingUser = await UserFactory.create(); + + const response = await client.post('/user/account').loginAs(user).form({ + username: existingUser.username, + email: user.email, + lastname: user.lastname, + }); + + response.assertTextIncludes( + 'value="unique validation failure" placeholder="Name"', + ); + }); + + test('returns a validation error for email when there is another user with same email', async ({ + client, + }) => { + const user = await UserFactory.create(); + const existingUser = await UserFactory.create(); + + const response = await client.post('/user/account').loginAs(user).form({ + username: user.username, + email: existingUser.email, + lastname: user.lastname, + }); + + response.assertTextIncludes( + 'value="unique validation failure" placeholder="E-Mail"', + ); + }); + + test('updates user data and ensures the data is persisted', async ({ + client, + assert, + }) => { + const user = await UserFactory.create(); + const response = await client.post('/user/account').loginAs(user).form({ + username: 'edited-username', + email: 'edited-email', + lastname: 'edited-lastname', + }); + + response.assertStatus(200); + + // Ensure updated data is displayed on account page + response.assertTextIncludes('edited-username'); + response.assertTextIncludes('edited-email'); + response.assertTextIncludes('edited-lastname'); + + // Ensure updated data is persisted in database + const updatedUser = await User.findBy('id', user.id); + assert.equal(updatedUser?.username, 'edited-username'); + assert.equal(updatedUser?.email, 'edited-email'); + assert.equal(updatedUser?.lastname, 'edited-lastname'); + }); + + test('updates user password and ensures the user can still login', async ({ + client, + }) => { + const user = await UserFactory.create(); + const response = await client.post('/user/account').loginAs(user).form({ + username: user.username, + email: user.email, + lastname: user.lastname, + password: 'modified-password-account-page', + }); + + response.assertStatus(200); + + const loginResponse = await client.post('/user/login').fields({ + mail: user.email, + password: 'modified-password-account-page', + }); + + loginResponse.assertRedirectsTo('/user/account'); + }); +}); diff --git a/tests/functional/dashboard/data.spec.ts b/tests/functional/dashboard/data.spec.ts new file mode 100644 index 0000000..1a0e7ad --- /dev/null +++ b/tests/functional/dashboard/data.spec.ts @@ -0,0 +1,31 @@ +import { test } from '@japa/runner'; +import UserFactory from 'Database/factories/UserFactory'; + +test.group('Dashboard / Data page', () => { + test('redirects to /user/login when accessing /user/data as guest', async ({ + client, + }) => { + const response = await client.get('/user/data'); + + response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL + }); + + test('ensure the correct data is shown on the page', async ({ client }) => { + const user = await UserFactory.create(); + const response = await client.get('/user/data').loginAs(user); + + response.assertStatus(200); + response.assertTextIncludes(user.email); + response.assertTextIncludes(user.username); + response.assertTextIncludes(user.lastname); + response.assertTextIncludes( + user.created_at.toFormat('yyyy-MM-dd HH:mm:ss'), + ); + response.assertTextIncludes( + user.updated_at.toFormat('yyyy-MM-dd HH:mm:ss'), + ); + }); + + // TODO: Add test to include services. + // TODO: Add test to include workspaces. +}); diff --git a/tests/functional/dashboard/delete.spec.ts b/tests/functional/dashboard/delete.spec.ts new file mode 100644 index 0000000..ae3f0e6 --- /dev/null +++ b/tests/functional/dashboard/delete.spec.ts @@ -0,0 +1,37 @@ +import { test } from '@japa/runner'; +import User from 'App/Models/User'; +import UserFactory from 'Database/factories/UserFactory'; + +test.group('Dashboard / Delete account page', () => { + test('redirects to /user/login when accessing /user/delete as guest', async ({ + client, + }) => { + const response = await client.get('/user/delete'); + + response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL + }); + + test('returns a 200 opening the delete route while logged in', async ({ + client, + }) => { + const user = await UserFactory.create(); + const response = await client.get('/user/delete').loginAs(user); + + response.assertStatus(200); + response.assertTextIncludes('Delete your account'); + }); + + test('returns a 200 opening the delete route while logged in', async ({ + client, + assert, + }) => { + const user = await UserFactory.create(); + const response = await client.post('/user/delete').loginAs(user); + + response.assertRedirectsTo('/user/login'); + // This asserts the session is deleted as well + response.assertSessionMissing('auth_web'); + + assert.isNull(await User.find(user.id)); + }); +}); diff --git a/tests/functional/dashboard/disabled-dashboard.spec.ts b/tests/functional/dashboard/disabled-dashboard.spec.ts new file mode 100644 index 0000000..cc9052c --- /dev/null +++ b/tests/functional/dashboard/disabled-dashboard.spec.ts @@ -0,0 +1,71 @@ +import { test } from '@japa/runner'; +import Config from '@ioc:Adonis/Core/Config'; + +const disabledDashboardMessage = + 'The user dashboard is disabled on this server\n\nIf you are the server owner, please set IS_DASHBOARD_ENABLED to true to enable the dashboard.'; + +test.group('Dashboard / Disabled dashboard', group => { + group.setup(() => { + Config.set('dashboard.enabled', false); + }); + + group.teardown(() => { + Config.set('dashboard.enabled', true); + }); + + test('Login page returns disabled dashboard message', async ({ client }) => { + const response = await client.get('/user/login'); + + response.assertTextIncludes(disabledDashboardMessage); + }); + + test('Forgot password page returns disabled dashboard message', async ({ + client, + }) => { + const response = await client.get('/user/forgot'); + + response.assertTextIncludes(disabledDashboardMessage); + }); + + test('Reset password page returns disabled dashboard message', async ({ + client, + }) => { + const response = await client.get('/user/reset'); + + response.assertTextIncludes(disabledDashboardMessage); + }); + + test('Account page returns disabled dashboard message', async ({ + client, + }) => { + const response = await client.get('/user/account'); + + response.assertTextIncludes(disabledDashboardMessage); + }); + + test('Data page returns disabled dashboard message', async ({ client }) => { + const response = await client.get('/user/data'); + + response.assertTextIncludes(disabledDashboardMessage); + }); + + test('Export page returns disabled dashboard message', async ({ client }) => { + const response = await client.get('/user/export'); + + response.assertTextIncludes(disabledDashboardMessage); + }); + + test('Transfer page returns disabled dashboard message', async ({ + client, + }) => { + const response = await client.get('/user/transfer'); + + response.assertTextIncludes(disabledDashboardMessage); + }); + + test('Logout page returns disabled dashboard message', async ({ client }) => { + const response = await client.get('/user/logout'); + + response.assertTextIncludes(disabledDashboardMessage); + }); +}); diff --git a/tests/functional/dashboard/export.spec.ts b/tests/functional/dashboard/export.spec.ts new file mode 100644 index 0000000..4250622 --- /dev/null +++ b/tests/functional/dashboard/export.spec.ts @@ -0,0 +1,61 @@ +import { test } from '@japa/runner'; +import UserFactory from 'Database/factories/UserFactory'; + +test.group('Dashboard / Export page', () => { + test('redirects to /user/login when accessing /user/transfer as guest', async ({ + client, + }) => { + const response = await client.get('/user/transfer'); + + response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL + }); + + test('returns a correct export with user data', async ({ + assert, + client, + }) => { + const user = await UserFactory.create(); + const response = await client.get('/user/export').loginAs(user); + + response.assertStatus(200); + const exportData = JSON.parse(response.text()); + + assert.equal(exportData.username, user.username); + assert.equal(exportData.lastname, user.lastname); + assert.equal(exportData.mail, user.email); + }); + + // TODO: We can improve this test by hard checking the export data + test('returns a correct export with service data', async ({ + assert, + client, + }) => { + const user = await UserFactory.with('services', 5).create(); + const response = await client.get('/user/export').loginAs(user); + + response.assertStatus(200); + const exportData = JSON.parse(response.text()); + + assert.equal(exportData.username, user.username); + assert.equal(exportData.lastname, user.lastname); + assert.equal(exportData.mail, user.email); + assert.equal(exportData.services.length, user.services.length); + }); + + // TODO: We can improve this test by hard checking the export data + test('returns a correct export with workspace data', async ({ + assert, + client, + }) => { + const user = await UserFactory.with('workspaces', 5).create(); + const response = await client.get('/user/export').loginAs(user); + + response.assertStatus(200); + const exportData = JSON.parse(response.text()); + + assert.equal(exportData.username, user.username); + assert.equal(exportData.lastname, user.lastname); + assert.equal(exportData.mail, user.email); + assert.equal(exportData.workspaces.length, user.workspaces.length); + }); +}); diff --git a/tests/functional/dashboard/forgot-password.spec.ts b/tests/functional/dashboard/forgot-password.spec.ts new file mode 100644 index 0000000..9dcec5a --- /dev/null +++ b/tests/functional/dashboard/forgot-password.spec.ts @@ -0,0 +1,70 @@ +import { test } from '@japa/runner'; +import Event from '@ioc:Adonis/Core/Event'; +import UserFactory from 'Database/factories/UserFactory'; + +test.group('Dashboard / Forgot password page', () => { + test('returns a 200 opening the forgot password route', async ({ + client, + }) => { + const response = await client.get('/user/forgot'); + + response.assertStatus(200); + response.assertTextIncludes('Forgot Password?'); + }); + + test('returns `Please enter a valid email address` when providing invalid email', async ({ + client, + }) => { + const response = await client.post('/user/forgot').fields({ + mail: 'invalid', + }); + + response.assertStatus(200); + response.assertTextIncludes('Please enter a valid email address'); + }); + + test('returns `email send when exists` without forgot:password event', async ({ + client, + assert, + }) => { + const emitter = Event.fake(); + + const response = await client.post('/user/forgot').fields({ + mail: 'test@ferdium.org', + }); + + response.assertStatus(200); + response.assertTextIncludes( + 'If your provided E-Mail address is linked to an account, we have just sent an E-Mail to that address.', + ); + + assert.isFalse(emitter.exists('forgot:password')); + }); + + test('returns `email send when exists` and trigger forgot:password event', async ({ + client, + assert, + }) => { + const emitter = Event.fake(); + const user = await UserFactory.merge({ + email: 'test+forgot_password@ferdium.org', + }).create(); + + const response = await client.post('/user/forgot').fields({ + mail: 'test+forgot_password@ferdium.org', + }); + + response.assertStatus(200); + response.assertTextIncludes( + 'If your provided E-Mail address is linked to an account, we have just sent an E-Mail to that address.', + ); + + assert.isTrue( + emitter.exists( + event => + event.name === 'forgot:password' && + event.data.user.email === user.email, + ), + ); + }); +}); diff --git a/tests/functional/dashboard/import-stubs/invalid.json b/tests/functional/dashboard/import-stubs/invalid.json new file mode 100644 index 0000000..86d11fa --- /dev/null +++ b/tests/functional/dashboard/import-stubs/invalid.json @@ -0,0 +1 @@ +Lorum ipsum diff --git a/tests/functional/dashboard/import-stubs/random-file.txt b/tests/functional/dashboard/import-stubs/random-file.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/functional/dashboard/import-stubs/services-only.ferdi-data b/tests/functional/dashboard/import-stubs/services-only.ferdi-data new file mode 100644 index 0000000..d95f3ef --- /dev/null +++ b/tests/functional/dashboard/import-stubs/services-only.ferdi-data @@ -0,0 +1,38 @@ +{ + "username": "John", + "lastname": "Doe", + "mail": "john.doe@ferdium.org", + "services": [ + { + "id": 5641, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 2134, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 5343, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + } + ], + "workspaces": [] +} diff --git a/tests/functional/dashboard/import-stubs/services-only.ferdium-data b/tests/functional/dashboard/import-stubs/services-only.ferdium-data new file mode 100644 index 0000000..d95f3ef --- /dev/null +++ b/tests/functional/dashboard/import-stubs/services-only.ferdium-data @@ -0,0 +1,38 @@ +{ + "username": "John", + "lastname": "Doe", + "mail": "john.doe@ferdium.org", + "services": [ + { + "id": 5641, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 2134, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 5343, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + } + ], + "workspaces": [] +} diff --git a/tests/functional/dashboard/import-stubs/services-only.json b/tests/functional/dashboard/import-stubs/services-only.json new file mode 100644 index 0000000..d95f3ef --- /dev/null +++ b/tests/functional/dashboard/import-stubs/services-only.json @@ -0,0 +1,38 @@ +{ + "username": "John", + "lastname": "Doe", + "mail": "john.doe@ferdium.org", + "services": [ + { + "id": 5641, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 2134, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 5343, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + } + ], + "workspaces": [] +} diff --git a/tests/functional/dashboard/import-stubs/services-workspaces.ferdi-data b/tests/functional/dashboard/import-stubs/services-workspaces.ferdi-data new file mode 100644 index 0000000..750e6ca --- /dev/null +++ b/tests/functional/dashboard/import-stubs/services-workspaces.ferdi-data @@ -0,0 +1,61 @@ +{ + "username": "John", + "lastname": "Doe", + "mail": "john.doe@ferdium.org", + "services": [ + { + "id": 5641, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 2134, + "userId": "1234", + "serviceId": "79769de5-a998-4af1-b7d0-89956a15b0ed", + "name": "random-service-2", + "recipeId": "random-service-2", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 5343, + "userId": "1234", + "serviceId": "0ac973f8-40dc-4760-b2c2-55e1d2943747", + "name": "random-service-3", + "recipeId": "random-service-3", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + } + ], + "workspaces": [ + { + "name": "workspace1", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace1\"}" + }, + { + "name": "workspace2", + "order": 0, + "services": ["d6901fff-ec44-4251-93de-d7103ed9c44b", "79769de5-a998-4af1-b7d0-89956a15b0ed"], + "data": "{\"name\":\"workspace2\"}" + }, + { + "name": "workspace3", + "order": 0, + "services": [ + "d6901fff-ec44-4251-93de-d7103ed9c44b", + "79769de5-a998-4af1-b7d0-89956a15b0ed", + "0ac973f8-40dc-4760-b2c2-55e1d2943747" + ], + "data": "{\"name\":\"workspace3\"}" + } + ] +} diff --git a/tests/functional/dashboard/import-stubs/services-workspaces.ferdium-data b/tests/functional/dashboard/import-stubs/services-workspaces.ferdium-data new file mode 100644 index 0000000..e999c0d --- /dev/null +++ b/tests/functional/dashboard/import-stubs/services-workspaces.ferdium-data @@ -0,0 +1,61 @@ +{ + "username": "John", + "lastname": "Doe", + "mail": "john.doe@ferdium.org", + "services": [ + { + "id": 5641, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 2134, + "userId": "1234", + "serviceId": "79769de5-a998-4af1-b7d0-89956a15b0ed", + "name": "random-service-2", + "recipeId": "random-service-2", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 5343, + "userId": "1234", + "serviceId": "0ac973f8-40dc-4760-b2c2-55e1d2943747", + "name": "random-service-3", + "recipeId": "random-service-2", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + } + ], + "workspaces": [ + { + "name": "workspace1", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace1\"}" + }, + { + "name": "workspace2", + "order": 0, + "services": ["d6901fff-ec44-4251-93de-d7103ed9c44b", "79769de5-a998-4af1-b7d0-89956a15b0ed"], + "data": "{\"name\":\"workspace2\"}" + }, + { + "name": "workspace3", + "order": 0, + "services": [ + "d6901fff-ec44-4251-93de-d7103ed9c44b", + "79769de5-a998-4af1-b7d0-89956a15b0ed", + "0ac973f8-40dc-4760-b2c2-55e1d2943747" + ], + "data": "{\"name\":\"workspace3\"}" + } + ] +} diff --git a/tests/functional/dashboard/import-stubs/services-workspaces.json b/tests/functional/dashboard/import-stubs/services-workspaces.json new file mode 100644 index 0000000..54c6889 --- /dev/null +++ b/tests/functional/dashboard/import-stubs/services-workspaces.json @@ -0,0 +1,64 @@ +{ + "username": "John", + "lastname": "Doe", + "mail": "john.doe@ferdium.org", + "services": [ + { + "id": 5641, + "userId": "1234", + "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", + "name": "random-service-1", + "recipeId": "random-service-1", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 2134, + "userId": "1234", + "serviceId": "79769de5-a998-4af1-b7d0-89956a15b0ed", + "name": "random-service-2", + "recipeId": "random-service-2", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + }, + { + "id": 5343, + "userId": "1234", + "serviceId": "0ac973f8-40dc-4760-b2c2-55e1d2943747", + "name": "random-service-3", + "recipeId": "random-service-3", + "settings": "{}", + "created_at": "2022-06-21 08:29:13", + "updated_at": "2022-07-19 15:47:16" + } + ], + "workspaces": [ + { + "name": "workspace1", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace1\"}" + }, + { + "name": "workspace2", + "order": 0, + "services": [ + "d6901fff-ec44-4251-93de-d7103ed9c44b", + "79769de5-a998-4af1-b7d0-89956a15b0ed" + ], + "data": "{\"name\":\"workspace2\"}" + }, + { + "name": "workspace3", + "order": 0, + "services": [ + "d6901fff-ec44-4251-93de-d7103ed9c44b", + "79769de5-a998-4af1-b7d0-89956a15b0ed", + "0ac973f8-40dc-4760-b2c2-55e1d2943747" + ], + "data": "{\"name\":\"workspace3\"}" + } + ] +} diff --git a/tests/functional/dashboard/import-stubs/valid-no-data.json b/tests/functional/dashboard/import-stubs/valid-no-data.json new file mode 100644 index 0000000..8d4816f --- /dev/null +++ b/tests/functional/dashboard/import-stubs/valid-no-data.json @@ -0,0 +1,3 @@ +{ + "test": "value" +} diff --git a/tests/functional/dashboard/import-stubs/workspaces-only.ferdi-data b/tests/functional/dashboard/import-stubs/workspaces-only.ferdi-data new file mode 100644 index 0000000..13ea9c6 --- /dev/null +++ b/tests/functional/dashboard/import-stubs/workspaces-only.ferdi-data @@ -0,0 +1,26 @@ +{ + "username": "John", + "lastname": "Doe", + "mail": "john.doe@ferdium.org", + "services": [], + "workspaces": [ + { + "name": "workspace1", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace1\"}" + }, + { + "name": "workspace2", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace2\"}" + }, + { + "name": "workspace3", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace3\"}" + } + ] +} diff --git a/tests/functional/dashboard/import-stubs/workspaces-only.ferdium-data b/tests/functional/dashboard/import-stubs/workspaces-only.ferdium-data new file mode 100644 index 0000000..13ea9c6 --- /dev/null +++ b/tests/functional/dashboard/import-stubs/workspaces-only.ferdium-data @@ -0,0 +1,26 @@ +{ + "username": "John", + "lastname": "Doe", + "mail": "john.doe@ferdium.org", + "services": [], + "workspaces": [ + { + "name": "workspace1", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace1\"}" + }, + { + "name": "workspace2", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace2\"}" + }, + { + "name": "workspace3", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace3\"}" + } + ] +} diff --git a/tests/functional/dashboard/import-stubs/workspaces-only.json b/tests/functional/dashboard/import-stubs/workspaces-only.json new file mode 100644 index 0000000..13ea9c6 --- /dev/null +++ b/tests/functional/dashboard/import-stubs/workspaces-only.json @@ -0,0 +1,26 @@ +{ + "username": "John", + "lastname": "Doe", + "mail": "john.doe@ferdium.org", + "services": [], + "workspaces": [ + { + "name": "workspace1", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace1\"}" + }, + { + "name": "workspace2", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace2\"}" + }, + { + "name": "workspace3", + "order": 0, + "services": [], + "data": "{\"name\":\"workspace3\"}" + } + ] +} diff --git a/tests/functional/dashboard/login.spec.ts b/tests/functional/dashboard/login.spec.ts new file mode 100644 index 0000000..adae66a --- /dev/null +++ b/tests/functional/dashboard/login.spec.ts @@ -0,0 +1,65 @@ +import { test } from '@japa/runner'; +import UserFactory from 'Database/factories/UserFactory'; + +test.group('Dashboard / Login page', () => { + test('returns a 200 opening the login route', async ({ client }) => { + const response = await client.get('/user/login'); + + response.assertStatus(200); + }); + + test('returns `invalid mail or password` when validation fails', async ({ + client, + }) => { + const response = await client.post('/user/login').fields({ + mail: 'invalid', + password: 'invalid', + }); + + response.assertRedirectsTo('/user/login'); + response.assertTextIncludes('Invalid mail or password'); + }); + + test('returns `invalid mail or password` when account is not found', async ({ + client, + }) => { + const response = await client.post('/user/login').fields({ + mail: 'test+notexistingpassword@ferdium.org', + password: 'notexistingpassword', + }); + + response.assertRedirectsTo('/user/login'); + response.assertTextIncludes('Invalid mail or password'); + }); + + test('returns `invalid mail or password` when password is not valid', async ({ + client, + }) => { + await UserFactory.merge({ + email: 'test@ferdium.org', + }).create(); + + const response = await client.post('/user/login').fields({ + mail: 'test+invalid_password@ferdium.org', + password: 'invalid_password', + }); + + response.assertRedirectsTo('/user/login'); + response.assertTextIncludes('Invalid mail or password'); + }); + + test('redirects to account page when user is able to login', async ({ + client, + }) => { + await UserFactory.merge({ + email: 'test+password@ferdium.org', + }).create(); + + const response = await client.post('/user/login').fields({ + mail: 'test+password@ferdium.org', + password: 'password', + }); + + response.assertRedirectsTo('/user/account'); + }); +}); diff --git a/tests/functional/dashboard/logout.spec.ts b/tests/functional/dashboard/logout.spec.ts new file mode 100644 index 0000000..a45ee72 --- /dev/null +++ b/tests/functional/dashboard/logout.spec.ts @@ -0,0 +1,21 @@ +import { test } from '@japa/runner'; +import UserFactory from 'Database/factories/UserFactory'; + +test.group('Dashboard / Log out page', () => { + test('redirects to /user/login when accessing /user/logout as guest', async ({ + client, + }) => { + const response = await client.get('/user/logout'); + + response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL + }); + + test('logs the user out when opening the page', async ({ client }) => { + const user = await UserFactory.create(); + const response = await client.get('/user/logout').loginAs(user); + + response.assertRedirectsTo('/user/login'); + // This asserts the session is deleted as well + response.assertSessionMissing('auth_web'); + }); +}); diff --git a/tests/functional/dashboard/reset-password.spec.ts b/tests/functional/dashboard/reset-password.spec.ts new file mode 100644 index 0000000..e488482 --- /dev/null +++ b/tests/functional/dashboard/reset-password.spec.ts @@ -0,0 +1,122 @@ +import { test } from '@japa/runner'; +import Token from 'App/Models/Token'; +import UserFactory from 'Database/factories/UserFactory'; +import TokenFactory from 'Database/factories/TokenFactory'; + +test.group('Dashboard / Reset password page', () => { + test('returns a `Invalid token` message when opening without a token', async ({ + client, + }) => { + const response = await client.get('/user/reset'); + + response.assertStatus(200); + response.assertTextIncludes('Invalid token'); + }); + + test('displays the form when a token is provided', async ({ client }) => { + const response = await client.get( + '/user/reset?token=randomtokenbutitworks', + ); + + response.assertStatus(200); + response.assertTextIncludes('Reset Your Password'); + }); + + test('returns `passwords do not match` message when passwords do not match', async ({ + client, + }) => { + const response = await client.post('/user/reset').fields({ + token: 'randomnotworkingtoken', + password: 'password', + password_confirmation: 'not_matching', + }); + + response.assertTextIncludes('Passwords do not match'); + }); + + test('returns `Cannot reset your password` when token does not exist', async ({ + client, + }) => { + const response = await client.post('/user/reset').fields({ + token: 'randomnotworkingtoken', + password: 'password', + password_confirmation: 'password', + }); + + response.assertTextIncludes('Cannot reset your password'); + }); + + test('returns `Cannot reset your password` when token is older than 24 hours', async ({ + client, + }) => { + const token = await TokenFactory.merge({ + // eslint-disable-next-line unicorn/no-await-expression-member + user_id: (await UserFactory.create()).id, + }) + .apply('old_token') + .create(); + + const response = await client.post('/user/reset').fields({ + token: token.token, + password: 'password', + password_confirmation: 'password', + }); + + response.assertTextIncludes('Cannot reset your password'); + }); + + test('returns `Cannot reset your password` when token is revoked', async ({ + client, + }) => { + const token = await TokenFactory.merge({ + // eslint-disable-next-line unicorn/no-await-expression-member + user_id: (await UserFactory.create()).id, + }) + .apply('revoked') + .create(); + + const response = await client.post('/user/reset').fields({ + token: token.token, + password: 'password', + password_confirmation: 'password', + }); + + response.assertTextIncludes('Cannot reset your password'); + }); + + test('correctly resets password and deletes token and able to login with new password', async ({ + client, + assert, + }) => { + const userEmail = 'working-reset-password-login@ferdium.org'; + const token = await TokenFactory.merge({ + user_id: + ( + await UserFactory.merge({ + email: userEmail, + }).create() + // prettier-ignore + // eslint-disable-next-line unicorn/no-await-expression-member + ).id, + }).create(); + + const response = await client.post('/user/reset').fields({ + token: token.token, + password: 'new_password', + password_confirmation: 'new_password', + }); + + // Assert response is as expected + response.assertTextIncludes('Successfully reset your password'); + + // Token should be deleted from database + assert.isNull(await Token.query().where('token', token.token).first()); + + const loginResponse = await client.post('/user/login').fields({ + mail: userEmail, + password: 'new_password', + }); + + loginResponse.assertRedirectsTo('/user/account'); + }); +}); diff --git a/tests/functional/dashboard/transfer.spec.ts b/tests/functional/dashboard/transfer.spec.ts new file mode 100644 index 0000000..0f8ee31 --- /dev/null +++ b/tests/functional/dashboard/transfer.spec.ts @@ -0,0 +1,222 @@ +import { test } from '@japa/runner'; +import UserFactory from 'Database/factories/UserFactory'; + +test.group('Dashboard / Transfer page', () => { + test('redirects to /user/login when accessing /user/transfer as guest', async ({ + client, + }) => { + const response = await client.get('/user/transfer'); + + response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL + }); + + test('returns a 200 opening the transfer route while being logged in', async ({ + client, + }) => { + const user = await UserFactory.create(); + const response = await client.get('/user/transfer').loginAs(user); + + response.assertStatus(200); + }); + + // TODO: Fix the following tests + + // test('returns a validation error when not uploading a file', async ({ + // client, + // }) => { + // const user = await UserFactory.create(); + // const response = await client.put('/user/transfer').loginAs(user); + + // response.assertTextIncludes('File missing'); + // }); + + // test('returns a validation error when trying to use anything but json', async ({ + // client, + // }) => { + // const user = await UserFactory.create(); + // const response = await client + // .put('/user/transfer') + // .loginAs(user) + // .file('file', 'tests/functional/dashboard/import-stubs/random-file.txt', { + // filename: 'random-file.txt', + // }); + + // response.assertTextIncludes('File missing'); + // }); + + // test('returns a validation error when trying to use anything valid json', async ({ + // client, + // }) => { + // const user = await UserFactory.create(); + // const response = await client + // .put('/user/transfer') + // .loginAs(user) + // .file('file', 'tests/functional/dashboard/import-stubs/invalid.json', { + // filename: 'invalid.json', + // }); + + // response.assertTextIncludes('Invalid Ferdium account file'); + // }); + + // test('returns a validation error when trying to use a json file with no ferdium structure', async ({ + // client, + // }) => { + // const user = await UserFactory.create(); + // const response = await client + // .put('/user/transfer') + // .loginAs(user) + // .file( + // 'file', + // 'tests/functional/dashboard/import-stubs/valid-no-data.json', + // { + // filename: 'valid-no-data.json', + // }, + // ); + + // response.assertTextIncludes('Invalid Ferdium account file (2)'); + // }); + + // test('correctly transfers data from json/ferdi-data and ferdium-data file with only workspaces', async ({ + // client, + // assert, + // }) => { + // // Repeat for all 3 file extension + // const files = [ + // 'workspaces-only.json', + // 'workspaces-only.ferdi-data', + // 'workspaces-only.ferdium-data', + // ]; + + // for (const file of files) { + // // eslint-disable-next-line no-await-in-loop + // const user = await UserFactory.create(); + // // eslint-disable-next-line no-await-in-loop + // const response = await client + // .put('/user/transfer') + // .loginAs(user) + // .file('file', `tests/functional/dashboard/import-stubs/${file}`, { + // filename: file, + // }); + + // response.assertTextIncludes( + // 'Your account has been imported, you can now login as usual!', + // ); + // // eslint-disable-next-line no-await-in-loop + // await user.refresh(); + + // // eslint-disable-next-line no-await-in-loop + // const workspacesForUser = await user.related('workspaces').query(); + // assert.equal(workspacesForUser.length, 3); + // } + // }); + + // test('correctly transfers data from json/ferdi-data and ferdium-data file with only services', async ({ + // client, + // assert, + // }) => { + // // Repeat for all 3 file extension + // const files = [ + // 'services-only.json', + // 'services-only.ferdi-data', + // 'services-only.ferdium-data', + // ]; + + // for (const file of files) { + // // eslint-disable-next-line no-await-in-loop + // const user = await UserFactory.create(); + // // eslint-disable-next-line no-await-in-loop + // const response = await client + // .put('/user/transfer') + // .loginAs(user) + // .file('file', `tests/functional/dashboard/import-stubs/${file}`, { + // filename: file, + // }); + + // response.assertTextIncludes( + // 'Your account has been imported, you can now login as usual!', + // ); + // // eslint-disable-next-line no-await-in-loop + // await user.refresh(); + + // // eslint-disable-next-line no-await-in-loop + // const servicesForUser = await user.related('services').query(); + // assert.equal(servicesForUser.length, 3); + // } + // }); + + // test('correctly transfers data from json/ferdi-data and ferdium-data file with workspaces and services', async ({ + // client, + // assert, + // }) => { + // // Repeat for all 3 file extension + // const files = [ + // 'services-workspaces.json', + // 'services-workspaces.ferdi-data', + // 'services-workspaces.ferdium-data', + // ]; + + // for (const file of files) { + // // eslint-disable-next-line no-await-in-loop + // const user = await UserFactory.create(); + // // eslint-disable-next-line no-await-in-loop + // const response = await client + // .put('/user/transfer') + // .loginAs(user) + // .file('file', `tests/functional/dashboard/import-stubs/${file}`, { + // filename: file, + // }); + + // response.assertTextIncludes( + // 'Your account has been imported, you can now login as usual!', + // ); + // // eslint-disable-next-line no-await-in-loop + // await user.refresh(); + + // // eslint-disable-next-line no-await-in-loop + // const servicesForUser = await user.related('services').query(); + // // eslint-disable-next-line no-await-in-loop + // const workspacesForUser = await user.related('workspaces').query(); + // assert.equal(servicesForUser.length, 3); + // assert.equal(workspacesForUser.length, 3); + + // const firstServiceUuid = servicesForUser.find( + // s => s.name === 'random-service-1', + // )?.serviceId; + // const secondServiceUuid = servicesForUser.find( + // s => s.name === 'random-service-2', + // )?.serviceId; + // const thirdServiceUUid = servicesForUser.find( + // s => s.name === 'random-service-3', + // )?.serviceId; + + // // Assert the first workspace to not have any services + // const firstWorkspace = workspacesForUser.find( + // w => w.name === 'workspace1', + // ); + // if (firstWorkspace?.services) { + // assert.empty(JSON.parse(firstWorkspace.services)); + // } + + // const secondWorkspace = workspacesForUser.find( + // w => w.name === 'workspace2', + // ); + // if (secondWorkspace?.services) { + // assert.deepEqual(JSON.parse(secondWorkspace.services), [ + // firstServiceUuid, + // secondServiceUuid, + // ]); + // } + + // const thirdWorkspace = workspacesForUser.find( + // w => w.name === 'workspace3', + // ); + // if (thirdWorkspace?.services) { + // assert.deepEqual(JSON.parse(thirdWorkspace.services), [ + // firstServiceUuid, + // secondServiceUuid, + // thirdServiceUUid, + // ]); + // } + // } + // }); +}); diff --git a/tests/functional/health.spec.ts b/tests/functional/health.spec.ts new file mode 100644 index 0000000..2f7e074 --- /dev/null +++ b/tests/functional/health.spec.ts @@ -0,0 +1,13 @@ +import { test } from '@japa/runner'; + +test.group('health page', () => { + test('returns a 200 response', async ({ client }) => { + const response = await client.get('/health'); + + response.assertStatus(200); + response.assertBodyContains({ + api: 'success', + db: 'success', + }); + }); +}); diff --git a/tests/functional/static-pages/home.spec.ts b/tests/functional/static-pages/home.spec.ts new file mode 100644 index 0000000..5054e05 --- /dev/null +++ b/tests/functional/static-pages/home.spec.ts @@ -0,0 +1,10 @@ +import { test } from '@japa/runner'; + +test.group('home page', () => { + test('returns a 200 response', async ({ client }) => { + const response = await client.get('/'); + + response.assertStatus(200); + response.assertTextIncludes('Go to account dashboard'); + }); +}); diff --git a/tests/functional/static-pages/privacy.spec.ts b/tests/functional/static-pages/privacy.spec.ts new file mode 100644 index 0000000..f1d5bec --- /dev/null +++ b/tests/functional/static-pages/privacy.spec.ts @@ -0,0 +1,10 @@ +import { test } from '@japa/runner'; + +test.group('privacy page', () => { + test('returns a 200 response', async ({ client }) => { + const response = await client.get('/privacy'); + + response.assertStatus(200); + response.assertTextIncludes('PRIVACY POLICY'); + }); +}); diff --git a/tests/functional/static-pages/terms.spec.ts b/tests/functional/static-pages/terms.spec.ts new file mode 100644 index 0000000..de990e4 --- /dev/null +++ b/tests/functional/static-pages/terms.spec.ts @@ -0,0 +1,10 @@ +import { test } from '@japa/runner'; + +test.group('terms page', () => { + test('returns a 200 response', async ({ client }) => { + const response = await client.get('/terms'); + + response.assertStatus(200); + response.assertTextIncludes('Terms of Service'); + }); +}); -- cgit v1.2.3-54-g00ecf