diff options
Diffstat (limited to 'tests/functional')
31 files changed, 1355 insertions, 0 deletions
diff --git a/tests/functional/api/register.spec.ts b/tests/functional/api/register.spec.ts new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/functional/api/register.spec.ts | |||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import { apiVersion } from '../../../config'; | ||
3 | |||
4 | test.group('API / Static / News', () => { | ||
5 | test('returns a 404 response when the requested versions does not exist', async ({ | ||
6 | client, | ||
7 | }) => { | ||
8 | const response = await client.get(`/${apiVersion}/announcements/illegal`); | ||
9 | |||
10 | response.assertStatus(404); | ||
11 | response.assertTextIncludes('No announcement found.'); | ||
12 | }); | ||
13 | |||
14 | test('returns a 200 response with default version file when specifying version as input', async ({ | ||
15 | client, | ||
16 | }) => { | ||
17 | const response = await client.get(`/${apiVersion}/announcements/version`); | ||
18 | |||
19 | response.assertStatus(200); | ||
20 | response.assertBody({ | ||
21 | main: { | ||
22 | headline: 'Example Announcement', | ||
23 | subHeadline: 'Configure your announcement here', | ||
24 | image: { | ||
25 | light: 'https://api.ferdium.org/assets/feature/light.png', | ||
26 | dark: 'https://api.ferdium.org/assets/feature/dark.png', | ||
27 | }, | ||
28 | text: 'Long description here', | ||
29 | cta: { | ||
30 | label: 'Click here to do something', | ||
31 | href: '/settings/app', | ||
32 | analytics: { | ||
33 | category: 'announcements-main', | ||
34 | action: 'event', | ||
35 | label: 'This does not get used', | ||
36 | }, | ||
37 | }, | ||
38 | }, | ||
39 | spotlight: { | ||
40 | title: 'Spotlight:', | ||
41 | subject: 'Show another feature', | ||
42 | text: 'Show another feature in the spotlight', | ||
43 | cta: { | ||
44 | label: 'Click here to do something', | ||
45 | href: '/settings/team', | ||
46 | analytics: { | ||
47 | category: 'announcements-spotlight', | ||
48 | action: 'event', | ||
49 | label: 'This does not get used', | ||
50 | }, | ||
51 | }, | ||
52 | }, | ||
53 | }); | ||
54 | }); | ||
55 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import { apiVersion } from '../../../config'; | ||
3 | |||
4 | const defaultResponse = { | ||
5 | isServiceProxyEnabled: true, | ||
6 | isWorkspaceEnabled: true, | ||
7 | isAnnouncementsEnabled: true, | ||
8 | isSettingsWSEnabled: false, | ||
9 | isMagicBarEnabled: true, | ||
10 | isTodosEnabled: true, | ||
11 | }; | ||
12 | |||
13 | test.group('API / Static / Features', () => { | ||
14 | test('returns a 200 response with empty array', async ({ client }) => { | ||
15 | const response = await client.get(`/${apiVersion}/features`); | ||
16 | |||
17 | response.assertStatus(200); | ||
18 | response.assertBody(defaultResponse); | ||
19 | }); | ||
20 | |||
21 | test('returns a 200 response with expected object when calling with :mode', async ({ | ||
22 | client, | ||
23 | }) => { | ||
24 | const response = await client.get(`/${apiVersion}/features/random`); | ||
25 | |||
26 | response.assertStatus(200); | ||
27 | response.assertBody(defaultResponse); | ||
28 | }); | ||
29 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import { apiVersion } from '../../../config'; | ||
3 | |||
4 | test.group('API / Static / News', () => { | ||
5 | test('returns a 200 response with empty array', async ({ client }) => { | ||
6 | const response = await client.get(`/${apiVersion}/news`); | ||
7 | |||
8 | response.assertStatus(200); | ||
9 | response.assertBody([]); | ||
10 | }); | ||
11 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import { apiVersion } from '../../../config'; | ||
3 | |||
4 | test.group('API / Static / Services', () => { | ||
5 | test('returns a 200 response with empty array', async ({ client }) => { | ||
6 | const response = await client.get(`/${apiVersion}/services`); | ||
7 | |||
8 | response.assertStatus(200); | ||
9 | response.assertBody([]); | ||
10 | }); | ||
11 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import User from 'App/Models/User'; | ||
3 | import UserFactory from 'Database/factories/UserFactory'; | ||
4 | |||
5 | test.group('Dashboard / Account page', () => { | ||
6 | test('redirects to /user/login when accessing /user/account as guest', async ({ | ||
7 | client, | ||
8 | }) => { | ||
9 | const response = await client.get('/user/account'); | ||
10 | |||
11 | response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL | ||
12 | }); | ||
13 | |||
14 | test('returns a 200 opening the account route while logged in', async ({ | ||
15 | client, | ||
16 | }) => { | ||
17 | const user = await UserFactory.create(); | ||
18 | const response = await client.get('/user/account').loginAs(user); | ||
19 | |||
20 | response.assertStatus(200); | ||
21 | response.assertTextIncludes('Your Ferdium account'); | ||
22 | |||
23 | response.assertTextIncludes(user.email); | ||
24 | response.assertTextIncludes(user.username); | ||
25 | response.assertTextIncludes(user.lastname); | ||
26 | }); | ||
27 | |||
28 | test('returns a validation error for all fields when missing', async ({ | ||
29 | client, | ||
30 | }) => { | ||
31 | const user = await UserFactory.create(); | ||
32 | const response = await client.post('/user/account').loginAs(user); | ||
33 | |||
34 | response.assertTextIncludes( | ||
35 | 'value="required validation failed,required validation failed" placeholder="E-Mail"', | ||
36 | ); | ||
37 | response.assertTextIncludes( | ||
38 | 'value="required validation failed,required validation failed" placeholder="Name"', | ||
39 | ); | ||
40 | response.assertTextIncludes( | ||
41 | 'value="required validation failed,required validation failed" placeholder="Last Name"', | ||
42 | ); | ||
43 | }); | ||
44 | |||
45 | test('returns a validation error for username when there is another user with same username', async ({ | ||
46 | client, | ||
47 | }) => { | ||
48 | const user = await UserFactory.create(); | ||
49 | const existingUser = await UserFactory.create(); | ||
50 | |||
51 | const response = await client.post('/user/account').loginAs(user).form({ | ||
52 | username: existingUser.username, | ||
53 | email: user.email, | ||
54 | lastname: user.lastname, | ||
55 | }); | ||
56 | |||
57 | response.assertTextIncludes( | ||
58 | 'value="unique validation failure" placeholder="Name"', | ||
59 | ); | ||
60 | }); | ||
61 | |||
62 | test('returns a validation error for email when there is another user with same email', async ({ | ||
63 | client, | ||
64 | }) => { | ||
65 | const user = await UserFactory.create(); | ||
66 | const existingUser = await UserFactory.create(); | ||
67 | |||
68 | const response = await client.post('/user/account').loginAs(user).form({ | ||
69 | username: user.username, | ||
70 | email: existingUser.email, | ||
71 | lastname: user.lastname, | ||
72 | }); | ||
73 | |||
74 | response.assertTextIncludes( | ||
75 | 'value="unique validation failure" placeholder="E-Mail"', | ||
76 | ); | ||
77 | }); | ||
78 | |||
79 | test('updates user data and ensures the data is persisted', async ({ | ||
80 | client, | ||
81 | assert, | ||
82 | }) => { | ||
83 | const user = await UserFactory.create(); | ||
84 | const response = await client.post('/user/account').loginAs(user).form({ | ||
85 | username: 'edited-username', | ||
86 | email: 'edited-email', | ||
87 | lastname: 'edited-lastname', | ||
88 | }); | ||
89 | |||
90 | response.assertStatus(200); | ||
91 | |||
92 | // Ensure updated data is displayed on account page | ||
93 | response.assertTextIncludes('edited-username'); | ||
94 | response.assertTextIncludes('edited-email'); | ||
95 | response.assertTextIncludes('edited-lastname'); | ||
96 | |||
97 | // Ensure updated data is persisted in database | ||
98 | const updatedUser = await User.findBy('id', user.id); | ||
99 | assert.equal(updatedUser?.username, 'edited-username'); | ||
100 | assert.equal(updatedUser?.email, 'edited-email'); | ||
101 | assert.equal(updatedUser?.lastname, 'edited-lastname'); | ||
102 | }); | ||
103 | |||
104 | test('updates user password and ensures the user can still login', async ({ | ||
105 | client, | ||
106 | }) => { | ||
107 | const user = await UserFactory.create(); | ||
108 | const response = await client.post('/user/account').loginAs(user).form({ | ||
109 | username: user.username, | ||
110 | email: user.email, | ||
111 | lastname: user.lastname, | ||
112 | password: 'modified-password-account-page', | ||
113 | }); | ||
114 | |||
115 | response.assertStatus(200); | ||
116 | |||
117 | const loginResponse = await client.post('/user/login').fields({ | ||
118 | mail: user.email, | ||
119 | password: 'modified-password-account-page', | ||
120 | }); | ||
121 | |||
122 | loginResponse.assertRedirectsTo('/user/account'); | ||
123 | }); | ||
124 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import UserFactory from 'Database/factories/UserFactory'; | ||
3 | |||
4 | test.group('Dashboard / Data page', () => { | ||
5 | test('redirects to /user/login when accessing /user/data as guest', async ({ | ||
6 | client, | ||
7 | }) => { | ||
8 | const response = await client.get('/user/data'); | ||
9 | |||
10 | response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL | ||
11 | }); | ||
12 | |||
13 | test('ensure the correct data is shown on the page', async ({ client }) => { | ||
14 | const user = await UserFactory.create(); | ||
15 | const response = await client.get('/user/data').loginAs(user); | ||
16 | |||
17 | response.assertStatus(200); | ||
18 | response.assertTextIncludes(user.email); | ||
19 | response.assertTextIncludes(user.username); | ||
20 | response.assertTextIncludes(user.lastname); | ||
21 | response.assertTextIncludes( | ||
22 | user.created_at.toFormat('yyyy-MM-dd HH:mm:ss'), | ||
23 | ); | ||
24 | response.assertTextIncludes( | ||
25 | user.updated_at.toFormat('yyyy-MM-dd HH:mm:ss'), | ||
26 | ); | ||
27 | }); | ||
28 | |||
29 | // TODO: Add test to include services. | ||
30 | // TODO: Add test to include workspaces. | ||
31 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import User from 'App/Models/User'; | ||
3 | import UserFactory from 'Database/factories/UserFactory'; | ||
4 | |||
5 | test.group('Dashboard / Delete account page', () => { | ||
6 | test('redirects to /user/login when accessing /user/delete as guest', async ({ | ||
7 | client, | ||
8 | }) => { | ||
9 | const response = await client.get('/user/delete'); | ||
10 | |||
11 | response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL | ||
12 | }); | ||
13 | |||
14 | test('returns a 200 opening the delete route while logged in', async ({ | ||
15 | client, | ||
16 | }) => { | ||
17 | const user = await UserFactory.create(); | ||
18 | const response = await client.get('/user/delete').loginAs(user); | ||
19 | |||
20 | response.assertStatus(200); | ||
21 | response.assertTextIncludes('Delete your account'); | ||
22 | }); | ||
23 | |||
24 | test('returns a 200 opening the delete route while logged in', async ({ | ||
25 | client, | ||
26 | assert, | ||
27 | }) => { | ||
28 | const user = await UserFactory.create(); | ||
29 | const response = await client.post('/user/delete').loginAs(user); | ||
30 | |||
31 | response.assertRedirectsTo('/user/login'); | ||
32 | // This asserts the session is deleted as well | ||
33 | response.assertSessionMissing('auth_web'); | ||
34 | |||
35 | assert.isNull(await User.find(user.id)); | ||
36 | }); | ||
37 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import Config from '@ioc:Adonis/Core/Config'; | ||
3 | |||
4 | const disabledDashboardMessage = | ||
5 | '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.'; | ||
6 | |||
7 | test.group('Dashboard / Disabled dashboard', group => { | ||
8 | group.setup(() => { | ||
9 | Config.set('dashboard.enabled', false); | ||
10 | }); | ||
11 | |||
12 | group.teardown(() => { | ||
13 | Config.set('dashboard.enabled', true); | ||
14 | }); | ||
15 | |||
16 | test('Login page returns disabled dashboard message', async ({ client }) => { | ||
17 | const response = await client.get('/user/login'); | ||
18 | |||
19 | response.assertTextIncludes(disabledDashboardMessage); | ||
20 | }); | ||
21 | |||
22 | test('Forgot password page returns disabled dashboard message', async ({ | ||
23 | client, | ||
24 | }) => { | ||
25 | const response = await client.get('/user/forgot'); | ||
26 | |||
27 | response.assertTextIncludes(disabledDashboardMessage); | ||
28 | }); | ||
29 | |||
30 | test('Reset password page returns disabled dashboard message', async ({ | ||
31 | client, | ||
32 | }) => { | ||
33 | const response = await client.get('/user/reset'); | ||
34 | |||
35 | response.assertTextIncludes(disabledDashboardMessage); | ||
36 | }); | ||
37 | |||
38 | test('Account page returns disabled dashboard message', async ({ | ||
39 | client, | ||
40 | }) => { | ||
41 | const response = await client.get('/user/account'); | ||
42 | |||
43 | response.assertTextIncludes(disabledDashboardMessage); | ||
44 | }); | ||
45 | |||
46 | test('Data page returns disabled dashboard message', async ({ client }) => { | ||
47 | const response = await client.get('/user/data'); | ||
48 | |||
49 | response.assertTextIncludes(disabledDashboardMessage); | ||
50 | }); | ||
51 | |||
52 | test('Export page returns disabled dashboard message', async ({ client }) => { | ||
53 | const response = await client.get('/user/export'); | ||
54 | |||
55 | response.assertTextIncludes(disabledDashboardMessage); | ||
56 | }); | ||
57 | |||
58 | test('Transfer page returns disabled dashboard message', async ({ | ||
59 | client, | ||
60 | }) => { | ||
61 | const response = await client.get('/user/transfer'); | ||
62 | |||
63 | response.assertTextIncludes(disabledDashboardMessage); | ||
64 | }); | ||
65 | |||
66 | test('Logout page returns disabled dashboard message', async ({ client }) => { | ||
67 | const response = await client.get('/user/logout'); | ||
68 | |||
69 | response.assertTextIncludes(disabledDashboardMessage); | ||
70 | }); | ||
71 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import UserFactory from 'Database/factories/UserFactory'; | ||
3 | |||
4 | test.group('Dashboard / Export page', () => { | ||
5 | test('redirects to /user/login when accessing /user/transfer as guest', async ({ | ||
6 | client, | ||
7 | }) => { | ||
8 | const response = await client.get('/user/transfer'); | ||
9 | |||
10 | response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL | ||
11 | }); | ||
12 | |||
13 | test('returns a correct export with user data', async ({ | ||
14 | assert, | ||
15 | client, | ||
16 | }) => { | ||
17 | const user = await UserFactory.create(); | ||
18 | const response = await client.get('/user/export').loginAs(user); | ||
19 | |||
20 | response.assertStatus(200); | ||
21 | const exportData = JSON.parse(response.text()); | ||
22 | |||
23 | assert.equal(exportData.username, user.username); | ||
24 | assert.equal(exportData.lastname, user.lastname); | ||
25 | assert.equal(exportData.mail, user.email); | ||
26 | }); | ||
27 | |||
28 | // TODO: We can improve this test by hard checking the export data | ||
29 | test('returns a correct export with service data', async ({ | ||
30 | assert, | ||
31 | client, | ||
32 | }) => { | ||
33 | const user = await UserFactory.with('services', 5).create(); | ||
34 | const response = await client.get('/user/export').loginAs(user); | ||
35 | |||
36 | response.assertStatus(200); | ||
37 | const exportData = JSON.parse(response.text()); | ||
38 | |||
39 | assert.equal(exportData.username, user.username); | ||
40 | assert.equal(exportData.lastname, user.lastname); | ||
41 | assert.equal(exportData.mail, user.email); | ||
42 | assert.equal(exportData.services.length, user.services.length); | ||
43 | }); | ||
44 | |||
45 | // TODO: We can improve this test by hard checking the export data | ||
46 | test('returns a correct export with workspace data', async ({ | ||
47 | assert, | ||
48 | client, | ||
49 | }) => { | ||
50 | const user = await UserFactory.with('workspaces', 5).create(); | ||
51 | const response = await client.get('/user/export').loginAs(user); | ||
52 | |||
53 | response.assertStatus(200); | ||
54 | const exportData = JSON.parse(response.text()); | ||
55 | |||
56 | assert.equal(exportData.username, user.username); | ||
57 | assert.equal(exportData.lastname, user.lastname); | ||
58 | assert.equal(exportData.mail, user.email); | ||
59 | assert.equal(exportData.workspaces.length, user.workspaces.length); | ||
60 | }); | ||
61 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import Event from '@ioc:Adonis/Core/Event'; | ||
3 | import UserFactory from 'Database/factories/UserFactory'; | ||
4 | |||
5 | test.group('Dashboard / Forgot password page', () => { | ||
6 | test('returns a 200 opening the forgot password route', async ({ | ||
7 | client, | ||
8 | }) => { | ||
9 | const response = await client.get('/user/forgot'); | ||
10 | |||
11 | response.assertStatus(200); | ||
12 | response.assertTextIncludes('Forgot Password?'); | ||
13 | }); | ||
14 | |||
15 | test('returns `Please enter a valid email address` when providing invalid email', async ({ | ||
16 | client, | ||
17 | }) => { | ||
18 | const response = await client.post('/user/forgot').fields({ | ||
19 | mail: 'invalid', | ||
20 | }); | ||
21 | |||
22 | response.assertStatus(200); | ||
23 | response.assertTextIncludes('Please enter a valid email address'); | ||
24 | }); | ||
25 | |||
26 | test('returns `email send when exists` without forgot:password event', async ({ | ||
27 | client, | ||
28 | assert, | ||
29 | }) => { | ||
30 | const emitter = Event.fake(); | ||
31 | |||
32 | const response = await client.post('/user/forgot').fields({ | ||
33 | mail: 'test@ferdium.org', | ||
34 | }); | ||
35 | |||
36 | response.assertStatus(200); | ||
37 | response.assertTextIncludes( | ||
38 | 'If your provided E-Mail address is linked to an account, we have just sent an E-Mail to that address.', | ||
39 | ); | ||
40 | |||
41 | assert.isFalse(emitter.exists('forgot:password')); | ||
42 | }); | ||
43 | |||
44 | test('returns `email send when exists` and trigger forgot:password event', async ({ | ||
45 | client, | ||
46 | assert, | ||
47 | }) => { | ||
48 | const emitter = Event.fake(); | ||
49 | const user = await UserFactory.merge({ | ||
50 | email: 'test+forgot_password@ferdium.org', | ||
51 | }).create(); | ||
52 | |||
53 | const response = await client.post('/user/forgot').fields({ | ||
54 | mail: 'test+forgot_password@ferdium.org', | ||
55 | }); | ||
56 | |||
57 | response.assertStatus(200); | ||
58 | response.assertTextIncludes( | ||
59 | 'If your provided E-Mail address is linked to an account, we have just sent an E-Mail to that address.', | ||
60 | ); | ||
61 | |||
62 | assert.isTrue( | ||
63 | emitter.exists( | ||
64 | event => | ||
65 | event.name === 'forgot:password' && | ||
66 | event.data.user.email === user.email, | ||
67 | ), | ||
68 | ); | ||
69 | }); | ||
70 | }); | ||
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 --- /dev/null +++ b/tests/functional/dashboard/import-stubs/random-file.txt | |||
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 @@ | |||
1 | { | ||
2 | "username": "John", | ||
3 | "lastname": "Doe", | ||
4 | "mail": "john.doe@ferdium.org", | ||
5 | "services": [ | ||
6 | { | ||
7 | "id": 5641, | ||
8 | "userId": "1234", | ||
9 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
10 | "name": "random-service-1", | ||
11 | "recipeId": "random-service-1", | ||
12 | "settings": "{}", | ||
13 | "created_at": "2022-06-21 08:29:13", | ||
14 | "updated_at": "2022-07-19 15:47:16" | ||
15 | }, | ||
16 | { | ||
17 | "id": 2134, | ||
18 | "userId": "1234", | ||
19 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
20 | "name": "random-service-1", | ||
21 | "recipeId": "random-service-1", | ||
22 | "settings": "{}", | ||
23 | "created_at": "2022-06-21 08:29:13", | ||
24 | "updated_at": "2022-07-19 15:47:16" | ||
25 | }, | ||
26 | { | ||
27 | "id": 5343, | ||
28 | "userId": "1234", | ||
29 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
30 | "name": "random-service-1", | ||
31 | "recipeId": "random-service-1", | ||
32 | "settings": "{}", | ||
33 | "created_at": "2022-06-21 08:29:13", | ||
34 | "updated_at": "2022-07-19 15:47:16" | ||
35 | } | ||
36 | ], | ||
37 | "workspaces": [] | ||
38 | } | ||
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 @@ | |||
1 | { | ||
2 | "username": "John", | ||
3 | "lastname": "Doe", | ||
4 | "mail": "john.doe@ferdium.org", | ||
5 | "services": [ | ||
6 | { | ||
7 | "id": 5641, | ||
8 | "userId": "1234", | ||
9 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
10 | "name": "random-service-1", | ||
11 | "recipeId": "random-service-1", | ||
12 | "settings": "{}", | ||
13 | "created_at": "2022-06-21 08:29:13", | ||
14 | "updated_at": "2022-07-19 15:47:16" | ||
15 | }, | ||
16 | { | ||
17 | "id": 2134, | ||
18 | "userId": "1234", | ||
19 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
20 | "name": "random-service-1", | ||
21 | "recipeId": "random-service-1", | ||
22 | "settings": "{}", | ||
23 | "created_at": "2022-06-21 08:29:13", | ||
24 | "updated_at": "2022-07-19 15:47:16" | ||
25 | }, | ||
26 | { | ||
27 | "id": 5343, | ||
28 | "userId": "1234", | ||
29 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
30 | "name": "random-service-1", | ||
31 | "recipeId": "random-service-1", | ||
32 | "settings": "{}", | ||
33 | "created_at": "2022-06-21 08:29:13", | ||
34 | "updated_at": "2022-07-19 15:47:16" | ||
35 | } | ||
36 | ], | ||
37 | "workspaces": [] | ||
38 | } | ||
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 @@ | |||
1 | { | ||
2 | "username": "John", | ||
3 | "lastname": "Doe", | ||
4 | "mail": "john.doe@ferdium.org", | ||
5 | "services": [ | ||
6 | { | ||
7 | "id": 5641, | ||
8 | "userId": "1234", | ||
9 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
10 | "name": "random-service-1", | ||
11 | "recipeId": "random-service-1", | ||
12 | "settings": "{}", | ||
13 | "created_at": "2022-06-21 08:29:13", | ||
14 | "updated_at": "2022-07-19 15:47:16" | ||
15 | }, | ||
16 | { | ||
17 | "id": 2134, | ||
18 | "userId": "1234", | ||
19 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
20 | "name": "random-service-1", | ||
21 | "recipeId": "random-service-1", | ||
22 | "settings": "{}", | ||
23 | "created_at": "2022-06-21 08:29:13", | ||
24 | "updated_at": "2022-07-19 15:47:16" | ||
25 | }, | ||
26 | { | ||
27 | "id": 5343, | ||
28 | "userId": "1234", | ||
29 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
30 | "name": "random-service-1", | ||
31 | "recipeId": "random-service-1", | ||
32 | "settings": "{}", | ||
33 | "created_at": "2022-06-21 08:29:13", | ||
34 | "updated_at": "2022-07-19 15:47:16" | ||
35 | } | ||
36 | ], | ||
37 | "workspaces": [] | ||
38 | } | ||
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 @@ | |||
1 | { | ||
2 | "username": "John", | ||
3 | "lastname": "Doe", | ||
4 | "mail": "john.doe@ferdium.org", | ||
5 | "services": [ | ||
6 | { | ||
7 | "id": 5641, | ||
8 | "userId": "1234", | ||
9 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
10 | "name": "random-service-1", | ||
11 | "recipeId": "random-service-1", | ||
12 | "settings": "{}", | ||
13 | "created_at": "2022-06-21 08:29:13", | ||
14 | "updated_at": "2022-07-19 15:47:16" | ||
15 | }, | ||
16 | { | ||
17 | "id": 2134, | ||
18 | "userId": "1234", | ||
19 | "serviceId": "79769de5-a998-4af1-b7d0-89956a15b0ed", | ||
20 | "name": "random-service-2", | ||
21 | "recipeId": "random-service-2", | ||
22 | "settings": "{}", | ||
23 | "created_at": "2022-06-21 08:29:13", | ||
24 | "updated_at": "2022-07-19 15:47:16" | ||
25 | }, | ||
26 | { | ||
27 | "id": 5343, | ||
28 | "userId": "1234", | ||
29 | "serviceId": "0ac973f8-40dc-4760-b2c2-55e1d2943747", | ||
30 | "name": "random-service-3", | ||
31 | "recipeId": "random-service-3", | ||
32 | "settings": "{}", | ||
33 | "created_at": "2022-06-21 08:29:13", | ||
34 | "updated_at": "2022-07-19 15:47:16" | ||
35 | } | ||
36 | ], | ||
37 | "workspaces": [ | ||
38 | { | ||
39 | "name": "workspace1", | ||
40 | "order": 0, | ||
41 | "services": [], | ||
42 | "data": "{\"name\":\"workspace1\"}" | ||
43 | }, | ||
44 | { | ||
45 | "name": "workspace2", | ||
46 | "order": 0, | ||
47 | "services": ["d6901fff-ec44-4251-93de-d7103ed9c44b", "79769de5-a998-4af1-b7d0-89956a15b0ed"], | ||
48 | "data": "{\"name\":\"workspace2\"}" | ||
49 | }, | ||
50 | { | ||
51 | "name": "workspace3", | ||
52 | "order": 0, | ||
53 | "services": [ | ||
54 | "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
55 | "79769de5-a998-4af1-b7d0-89956a15b0ed", | ||
56 | "0ac973f8-40dc-4760-b2c2-55e1d2943747" | ||
57 | ], | ||
58 | "data": "{\"name\":\"workspace3\"}" | ||
59 | } | ||
60 | ] | ||
61 | } | ||
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 @@ | |||
1 | { | ||
2 | "username": "John", | ||
3 | "lastname": "Doe", | ||
4 | "mail": "john.doe@ferdium.org", | ||
5 | "services": [ | ||
6 | { | ||
7 | "id": 5641, | ||
8 | "userId": "1234", | ||
9 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
10 | "name": "random-service-1", | ||
11 | "recipeId": "random-service-1", | ||
12 | "settings": "{}", | ||
13 | "created_at": "2022-06-21 08:29:13", | ||
14 | "updated_at": "2022-07-19 15:47:16" | ||
15 | }, | ||
16 | { | ||
17 | "id": 2134, | ||
18 | "userId": "1234", | ||
19 | "serviceId": "79769de5-a998-4af1-b7d0-89956a15b0ed", | ||
20 | "name": "random-service-2", | ||
21 | "recipeId": "random-service-2", | ||
22 | "settings": "{}", | ||
23 | "created_at": "2022-06-21 08:29:13", | ||
24 | "updated_at": "2022-07-19 15:47:16" | ||
25 | }, | ||
26 | { | ||
27 | "id": 5343, | ||
28 | "userId": "1234", | ||
29 | "serviceId": "0ac973f8-40dc-4760-b2c2-55e1d2943747", | ||
30 | "name": "random-service-3", | ||
31 | "recipeId": "random-service-2", | ||
32 | "settings": "{}", | ||
33 | "created_at": "2022-06-21 08:29:13", | ||
34 | "updated_at": "2022-07-19 15:47:16" | ||
35 | } | ||
36 | ], | ||
37 | "workspaces": [ | ||
38 | { | ||
39 | "name": "workspace1", | ||
40 | "order": 0, | ||
41 | "services": [], | ||
42 | "data": "{\"name\":\"workspace1\"}" | ||
43 | }, | ||
44 | { | ||
45 | "name": "workspace2", | ||
46 | "order": 0, | ||
47 | "services": ["d6901fff-ec44-4251-93de-d7103ed9c44b", "79769de5-a998-4af1-b7d0-89956a15b0ed"], | ||
48 | "data": "{\"name\":\"workspace2\"}" | ||
49 | }, | ||
50 | { | ||
51 | "name": "workspace3", | ||
52 | "order": 0, | ||
53 | "services": [ | ||
54 | "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
55 | "79769de5-a998-4af1-b7d0-89956a15b0ed", | ||
56 | "0ac973f8-40dc-4760-b2c2-55e1d2943747" | ||
57 | ], | ||
58 | "data": "{\"name\":\"workspace3\"}" | ||
59 | } | ||
60 | ] | ||
61 | } | ||
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 @@ | |||
1 | { | ||
2 | "username": "John", | ||
3 | "lastname": "Doe", | ||
4 | "mail": "john.doe@ferdium.org", | ||
5 | "services": [ | ||
6 | { | ||
7 | "id": 5641, | ||
8 | "userId": "1234", | ||
9 | "serviceId": "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
10 | "name": "random-service-1", | ||
11 | "recipeId": "random-service-1", | ||
12 | "settings": "{}", | ||
13 | "created_at": "2022-06-21 08:29:13", | ||
14 | "updated_at": "2022-07-19 15:47:16" | ||
15 | }, | ||
16 | { | ||
17 | "id": 2134, | ||
18 | "userId": "1234", | ||
19 | "serviceId": "79769de5-a998-4af1-b7d0-89956a15b0ed", | ||
20 | "name": "random-service-2", | ||
21 | "recipeId": "random-service-2", | ||
22 | "settings": "{}", | ||
23 | "created_at": "2022-06-21 08:29:13", | ||
24 | "updated_at": "2022-07-19 15:47:16" | ||
25 | }, | ||
26 | { | ||
27 | "id": 5343, | ||
28 | "userId": "1234", | ||
29 | "serviceId": "0ac973f8-40dc-4760-b2c2-55e1d2943747", | ||
30 | "name": "random-service-3", | ||
31 | "recipeId": "random-service-3", | ||
32 | "settings": "{}", | ||
33 | "created_at": "2022-06-21 08:29:13", | ||
34 | "updated_at": "2022-07-19 15:47:16" | ||
35 | } | ||
36 | ], | ||
37 | "workspaces": [ | ||
38 | { | ||
39 | "name": "workspace1", | ||
40 | "order": 0, | ||
41 | "services": [], | ||
42 | "data": "{\"name\":\"workspace1\"}" | ||
43 | }, | ||
44 | { | ||
45 | "name": "workspace2", | ||
46 | "order": 0, | ||
47 | "services": [ | ||
48 | "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
49 | "79769de5-a998-4af1-b7d0-89956a15b0ed" | ||
50 | ], | ||
51 | "data": "{\"name\":\"workspace2\"}" | ||
52 | }, | ||
53 | { | ||
54 | "name": "workspace3", | ||
55 | "order": 0, | ||
56 | "services": [ | ||
57 | "d6901fff-ec44-4251-93de-d7103ed9c44b", | ||
58 | "79769de5-a998-4af1-b7d0-89956a15b0ed", | ||
59 | "0ac973f8-40dc-4760-b2c2-55e1d2943747" | ||
60 | ], | ||
61 | "data": "{\"name\":\"workspace3\"}" | ||
62 | } | ||
63 | ] | ||
64 | } | ||
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 @@ | |||
1 | { | ||
2 | "test": "value" | ||
3 | } | ||
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 @@ | |||
1 | { | ||
2 | "username": "John", | ||
3 | "lastname": "Doe", | ||
4 | "mail": "john.doe@ferdium.org", | ||
5 | "services": [], | ||
6 | "workspaces": [ | ||
7 | { | ||
8 | "name": "workspace1", | ||
9 | "order": 0, | ||
10 | "services": [], | ||
11 | "data": "{\"name\":\"workspace1\"}" | ||
12 | }, | ||
13 | { | ||
14 | "name": "workspace2", | ||
15 | "order": 0, | ||
16 | "services": [], | ||
17 | "data": "{\"name\":\"workspace2\"}" | ||
18 | }, | ||
19 | { | ||
20 | "name": "workspace3", | ||
21 | "order": 0, | ||
22 | "services": [], | ||
23 | "data": "{\"name\":\"workspace3\"}" | ||
24 | } | ||
25 | ] | ||
26 | } | ||
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 @@ | |||
1 | { | ||
2 | "username": "John", | ||
3 | "lastname": "Doe", | ||
4 | "mail": "john.doe@ferdium.org", | ||
5 | "services": [], | ||
6 | "workspaces": [ | ||
7 | { | ||
8 | "name": "workspace1", | ||
9 | "order": 0, | ||
10 | "services": [], | ||
11 | "data": "{\"name\":\"workspace1\"}" | ||
12 | }, | ||
13 | { | ||
14 | "name": "workspace2", | ||
15 | "order": 0, | ||
16 | "services": [], | ||
17 | "data": "{\"name\":\"workspace2\"}" | ||
18 | }, | ||
19 | { | ||
20 | "name": "workspace3", | ||
21 | "order": 0, | ||
22 | "services": [], | ||
23 | "data": "{\"name\":\"workspace3\"}" | ||
24 | } | ||
25 | ] | ||
26 | } | ||
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 @@ | |||
1 | { | ||
2 | "username": "John", | ||
3 | "lastname": "Doe", | ||
4 | "mail": "john.doe@ferdium.org", | ||
5 | "services": [], | ||
6 | "workspaces": [ | ||
7 | { | ||
8 | "name": "workspace1", | ||
9 | "order": 0, | ||
10 | "services": [], | ||
11 | "data": "{\"name\":\"workspace1\"}" | ||
12 | }, | ||
13 | { | ||
14 | "name": "workspace2", | ||
15 | "order": 0, | ||
16 | "services": [], | ||
17 | "data": "{\"name\":\"workspace2\"}" | ||
18 | }, | ||
19 | { | ||
20 | "name": "workspace3", | ||
21 | "order": 0, | ||
22 | "services": [], | ||
23 | "data": "{\"name\":\"workspace3\"}" | ||
24 | } | ||
25 | ] | ||
26 | } | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import UserFactory from 'Database/factories/UserFactory'; | ||
3 | |||
4 | test.group('Dashboard / Login page', () => { | ||
5 | test('returns a 200 opening the login route', async ({ client }) => { | ||
6 | const response = await client.get('/user/login'); | ||
7 | |||
8 | response.assertStatus(200); | ||
9 | }); | ||
10 | |||
11 | test('returns `invalid mail or password` when validation fails', async ({ | ||
12 | client, | ||
13 | }) => { | ||
14 | const response = await client.post('/user/login').fields({ | ||
15 | mail: 'invalid', | ||
16 | password: 'invalid', | ||
17 | }); | ||
18 | |||
19 | response.assertRedirectsTo('/user/login'); | ||
20 | response.assertTextIncludes('Invalid mail or password'); | ||
21 | }); | ||
22 | |||
23 | test('returns `invalid mail or password` when account is not found', async ({ | ||
24 | client, | ||
25 | }) => { | ||
26 | const response = await client.post('/user/login').fields({ | ||
27 | mail: 'test+notexistingpassword@ferdium.org', | ||
28 | password: 'notexistingpassword', | ||
29 | }); | ||
30 | |||
31 | response.assertRedirectsTo('/user/login'); | ||
32 | response.assertTextIncludes('Invalid mail or password'); | ||
33 | }); | ||
34 | |||
35 | test('returns `invalid mail or password` when password is not valid', async ({ | ||
36 | client, | ||
37 | }) => { | ||
38 | await UserFactory.merge({ | ||
39 | email: 'test@ferdium.org', | ||
40 | }).create(); | ||
41 | |||
42 | const response = await client.post('/user/login').fields({ | ||
43 | mail: 'test+invalid_password@ferdium.org', | ||
44 | password: 'invalid_password', | ||
45 | }); | ||
46 | |||
47 | response.assertRedirectsTo('/user/login'); | ||
48 | response.assertTextIncludes('Invalid mail or password'); | ||
49 | }); | ||
50 | |||
51 | test('redirects to account page when user is able to login', async ({ | ||
52 | client, | ||
53 | }) => { | ||
54 | await UserFactory.merge({ | ||
55 | email: 'test+password@ferdium.org', | ||
56 | }).create(); | ||
57 | |||
58 | const response = await client.post('/user/login').fields({ | ||
59 | mail: 'test+password@ferdium.org', | ||
60 | password: 'password', | ||
61 | }); | ||
62 | |||
63 | response.assertRedirectsTo('/user/account'); | ||
64 | }); | ||
65 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import UserFactory from 'Database/factories/UserFactory'; | ||
3 | |||
4 | test.group('Dashboard / Log out page', () => { | ||
5 | test('redirects to /user/login when accessing /user/logout as guest', async ({ | ||
6 | client, | ||
7 | }) => { | ||
8 | const response = await client.get('/user/logout'); | ||
9 | |||
10 | response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL | ||
11 | }); | ||
12 | |||
13 | test('logs the user out when opening the page', async ({ client }) => { | ||
14 | const user = await UserFactory.create(); | ||
15 | const response = await client.get('/user/logout').loginAs(user); | ||
16 | |||
17 | response.assertRedirectsTo('/user/login'); | ||
18 | // This asserts the session is deleted as well | ||
19 | response.assertSessionMissing('auth_web'); | ||
20 | }); | ||
21 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import Token from 'App/Models/Token'; | ||
3 | import UserFactory from 'Database/factories/UserFactory'; | ||
4 | import TokenFactory from 'Database/factories/TokenFactory'; | ||
5 | |||
6 | test.group('Dashboard / Reset password page', () => { | ||
7 | test('returns a `Invalid token` message when opening without a token', async ({ | ||
8 | client, | ||
9 | }) => { | ||
10 | const response = await client.get('/user/reset'); | ||
11 | |||
12 | response.assertStatus(200); | ||
13 | response.assertTextIncludes('Invalid token'); | ||
14 | }); | ||
15 | |||
16 | test('displays the form when a token is provided', async ({ client }) => { | ||
17 | const response = await client.get( | ||
18 | '/user/reset?token=randomtokenbutitworks', | ||
19 | ); | ||
20 | |||
21 | response.assertStatus(200); | ||
22 | response.assertTextIncludes('Reset Your Password'); | ||
23 | }); | ||
24 | |||
25 | test('returns `passwords do not match` message when passwords do not match', async ({ | ||
26 | client, | ||
27 | }) => { | ||
28 | const response = await client.post('/user/reset').fields({ | ||
29 | token: 'randomnotworkingtoken', | ||
30 | password: 'password', | ||
31 | password_confirmation: 'not_matching', | ||
32 | }); | ||
33 | |||
34 | response.assertTextIncludes('Passwords do not match'); | ||
35 | }); | ||
36 | |||
37 | test('returns `Cannot reset your password` when token does not exist', async ({ | ||
38 | client, | ||
39 | }) => { | ||
40 | const response = await client.post('/user/reset').fields({ | ||
41 | token: 'randomnotworkingtoken', | ||
42 | password: 'password', | ||
43 | password_confirmation: 'password', | ||
44 | }); | ||
45 | |||
46 | response.assertTextIncludes('Cannot reset your password'); | ||
47 | }); | ||
48 | |||
49 | test('returns `Cannot reset your password` when token is older than 24 hours', async ({ | ||
50 | client, | ||
51 | }) => { | ||
52 | const token = await TokenFactory.merge({ | ||
53 | // eslint-disable-next-line unicorn/no-await-expression-member | ||
54 | user_id: (await UserFactory.create()).id, | ||
55 | }) | ||
56 | .apply('old_token') | ||
57 | .create(); | ||
58 | |||
59 | const response = await client.post('/user/reset').fields({ | ||
60 | token: token.token, | ||
61 | password: 'password', | ||
62 | password_confirmation: 'password', | ||
63 | }); | ||
64 | |||
65 | response.assertTextIncludes('Cannot reset your password'); | ||
66 | }); | ||
67 | |||
68 | test('returns `Cannot reset your password` when token is revoked', async ({ | ||
69 | client, | ||
70 | }) => { | ||
71 | const token = await TokenFactory.merge({ | ||
72 | // eslint-disable-next-line unicorn/no-await-expression-member | ||
73 | user_id: (await UserFactory.create()).id, | ||
74 | }) | ||
75 | .apply('revoked') | ||
76 | .create(); | ||
77 | |||
78 | const response = await client.post('/user/reset').fields({ | ||
79 | token: token.token, | ||
80 | password: 'password', | ||
81 | password_confirmation: 'password', | ||
82 | }); | ||
83 | |||
84 | response.assertTextIncludes('Cannot reset your password'); | ||
85 | }); | ||
86 | |||
87 | test('correctly resets password and deletes token and able to login with new password', async ({ | ||
88 | client, | ||
89 | assert, | ||
90 | }) => { | ||
91 | const userEmail = 'working-reset-password-login@ferdium.org'; | ||
92 | const token = await TokenFactory.merge({ | ||
93 | user_id: | ||
94 | ( | ||
95 | await UserFactory.merge({ | ||
96 | email: userEmail, | ||
97 | }).create() | ||
98 | // prettier-ignore | ||
99 | // eslint-disable-next-line unicorn/no-await-expression-member | ||
100 | ).id, | ||
101 | }).create(); | ||
102 | |||
103 | const response = await client.post('/user/reset').fields({ | ||
104 | token: token.token, | ||
105 | password: 'new_password', | ||
106 | password_confirmation: 'new_password', | ||
107 | }); | ||
108 | |||
109 | // Assert response is as expected | ||
110 | response.assertTextIncludes('Successfully reset your password'); | ||
111 | |||
112 | // Token should be deleted from database | ||
113 | assert.isNull(await Token.query().where('token', token.token).first()); | ||
114 | |||
115 | const loginResponse = await client.post('/user/login').fields({ | ||
116 | mail: userEmail, | ||
117 | password: 'new_password', | ||
118 | }); | ||
119 | |||
120 | loginResponse.assertRedirectsTo('/user/account'); | ||
121 | }); | ||
122 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | import UserFactory from 'Database/factories/UserFactory'; | ||
3 | |||
4 | test.group('Dashboard / Transfer page', () => { | ||
5 | test('redirects to /user/login when accessing /user/transfer as guest', async ({ | ||
6 | client, | ||
7 | }) => { | ||
8 | const response = await client.get('/user/transfer'); | ||
9 | |||
10 | response.assertRedirectsTo('/user/login'); // Check if it redirects to the expected URL | ||
11 | }); | ||
12 | |||
13 | test('returns a 200 opening the transfer route while being logged in', async ({ | ||
14 | client, | ||
15 | }) => { | ||
16 | const user = await UserFactory.create(); | ||
17 | const response = await client.get('/user/transfer').loginAs(user); | ||
18 | |||
19 | response.assertStatus(200); | ||
20 | }); | ||
21 | |||
22 | // TODO: Fix the following tests | ||
23 | |||
24 | // test('returns a validation error when not uploading a file', async ({ | ||
25 | // client, | ||
26 | // }) => { | ||
27 | // const user = await UserFactory.create(); | ||
28 | // const response = await client.put('/user/transfer').loginAs(user); | ||
29 | |||
30 | // response.assertTextIncludes('File missing'); | ||
31 | // }); | ||
32 | |||
33 | // test('returns a validation error when trying to use anything but json', async ({ | ||
34 | // client, | ||
35 | // }) => { | ||
36 | // const user = await UserFactory.create(); | ||
37 | // const response = await client | ||
38 | // .put('/user/transfer') | ||
39 | // .loginAs(user) | ||
40 | // .file('file', 'tests/functional/dashboard/import-stubs/random-file.txt', { | ||
41 | // filename: 'random-file.txt', | ||
42 | // }); | ||
43 | |||
44 | // response.assertTextIncludes('File missing'); | ||
45 | // }); | ||
46 | |||
47 | // test('returns a validation error when trying to use anything valid json', async ({ | ||
48 | // client, | ||
49 | // }) => { | ||
50 | // const user = await UserFactory.create(); | ||
51 | // const response = await client | ||
52 | // .put('/user/transfer') | ||
53 | // .loginAs(user) | ||
54 | // .file('file', 'tests/functional/dashboard/import-stubs/invalid.json', { | ||
55 | // filename: 'invalid.json', | ||
56 | // }); | ||
57 | |||
58 | // response.assertTextIncludes('Invalid Ferdium account file'); | ||
59 | // }); | ||
60 | |||
61 | // test('returns a validation error when trying to use a json file with no ferdium structure', async ({ | ||
62 | // client, | ||
63 | // }) => { | ||
64 | // const user = await UserFactory.create(); | ||
65 | // const response = await client | ||
66 | // .put('/user/transfer') | ||
67 | // .loginAs(user) | ||
68 | // .file( | ||
69 | // 'file', | ||
70 | // 'tests/functional/dashboard/import-stubs/valid-no-data.json', | ||
71 | // { | ||
72 | // filename: 'valid-no-data.json', | ||
73 | // }, | ||
74 | // ); | ||
75 | |||
76 | // response.assertTextIncludes('Invalid Ferdium account file (2)'); | ||
77 | // }); | ||
78 | |||
79 | // test('correctly transfers data from json/ferdi-data and ferdium-data file with only workspaces', async ({ | ||
80 | // client, | ||
81 | // assert, | ||
82 | // }) => { | ||
83 | // // Repeat for all 3 file extension | ||
84 | // const files = [ | ||
85 | // 'workspaces-only.json', | ||
86 | // 'workspaces-only.ferdi-data', | ||
87 | // 'workspaces-only.ferdium-data', | ||
88 | // ]; | ||
89 | |||
90 | // for (const file of files) { | ||
91 | // // eslint-disable-next-line no-await-in-loop | ||
92 | // const user = await UserFactory.create(); | ||
93 | // // eslint-disable-next-line no-await-in-loop | ||
94 | // const response = await client | ||
95 | // .put('/user/transfer') | ||
96 | // .loginAs(user) | ||
97 | // .file('file', `tests/functional/dashboard/import-stubs/${file}`, { | ||
98 | // filename: file, | ||
99 | // }); | ||
100 | |||
101 | // response.assertTextIncludes( | ||
102 | // 'Your account has been imported, you can now login as usual!', | ||
103 | // ); | ||
104 | // // eslint-disable-next-line no-await-in-loop | ||
105 | // await user.refresh(); | ||
106 | |||
107 | // // eslint-disable-next-line no-await-in-loop | ||
108 | // const workspacesForUser = await user.related('workspaces').query(); | ||
109 | // assert.equal(workspacesForUser.length, 3); | ||
110 | // } | ||
111 | // }); | ||
112 | |||
113 | // test('correctly transfers data from json/ferdi-data and ferdium-data file with only services', async ({ | ||
114 | // client, | ||
115 | // assert, | ||
116 | // }) => { | ||
117 | // // Repeat for all 3 file extension | ||
118 | // const files = [ | ||
119 | // 'services-only.json', | ||
120 | // 'services-only.ferdi-data', | ||
121 | // 'services-only.ferdium-data', | ||
122 | // ]; | ||
123 | |||
124 | // for (const file of files) { | ||
125 | // // eslint-disable-next-line no-await-in-loop | ||
126 | // const user = await UserFactory.create(); | ||
127 | // // eslint-disable-next-line no-await-in-loop | ||
128 | // const response = await client | ||
129 | // .put('/user/transfer') | ||
130 | // .loginAs(user) | ||
131 | // .file('file', `tests/functional/dashboard/import-stubs/${file}`, { | ||
132 | // filename: file, | ||
133 | // }); | ||
134 | |||
135 | // response.assertTextIncludes( | ||
136 | // 'Your account has been imported, you can now login as usual!', | ||
137 | // ); | ||
138 | // // eslint-disable-next-line no-await-in-loop | ||
139 | // await user.refresh(); | ||
140 | |||
141 | // // eslint-disable-next-line no-await-in-loop | ||
142 | // const servicesForUser = await user.related('services').query(); | ||
143 | // assert.equal(servicesForUser.length, 3); | ||
144 | // } | ||
145 | // }); | ||
146 | |||
147 | // test('correctly transfers data from json/ferdi-data and ferdium-data file with workspaces and services', async ({ | ||
148 | // client, | ||
149 | // assert, | ||
150 | // }) => { | ||
151 | // // Repeat for all 3 file extension | ||
152 | // const files = [ | ||
153 | // 'services-workspaces.json', | ||
154 | // 'services-workspaces.ferdi-data', | ||
155 | // 'services-workspaces.ferdium-data', | ||
156 | // ]; | ||
157 | |||
158 | // for (const file of files) { | ||
159 | // // eslint-disable-next-line no-await-in-loop | ||
160 | // const user = await UserFactory.create(); | ||
161 | // // eslint-disable-next-line no-await-in-loop | ||
162 | // const response = await client | ||
163 | // .put('/user/transfer') | ||
164 | // .loginAs(user) | ||
165 | // .file('file', `tests/functional/dashboard/import-stubs/${file}`, { | ||
166 | // filename: file, | ||
167 | // }); | ||
168 | |||
169 | // response.assertTextIncludes( | ||
170 | // 'Your account has been imported, you can now login as usual!', | ||
171 | // ); | ||
172 | // // eslint-disable-next-line no-await-in-loop | ||
173 | // await user.refresh(); | ||
174 | |||
175 | // // eslint-disable-next-line no-await-in-loop | ||
176 | // const servicesForUser = await user.related('services').query(); | ||
177 | // // eslint-disable-next-line no-await-in-loop | ||
178 | // const workspacesForUser = await user.related('workspaces').query(); | ||
179 | // assert.equal(servicesForUser.length, 3); | ||
180 | // assert.equal(workspacesForUser.length, 3); | ||
181 | |||
182 | // const firstServiceUuid = servicesForUser.find( | ||
183 | // s => s.name === 'random-service-1', | ||
184 | // )?.serviceId; | ||
185 | // const secondServiceUuid = servicesForUser.find( | ||
186 | // s => s.name === 'random-service-2', | ||
187 | // )?.serviceId; | ||
188 | // const thirdServiceUUid = servicesForUser.find( | ||
189 | // s => s.name === 'random-service-3', | ||
190 | // )?.serviceId; | ||
191 | |||
192 | // // Assert the first workspace to not have any services | ||
193 | // const firstWorkspace = workspacesForUser.find( | ||
194 | // w => w.name === 'workspace1', | ||
195 | // ); | ||
196 | // if (firstWorkspace?.services) { | ||
197 | // assert.empty(JSON.parse(firstWorkspace.services)); | ||
198 | // } | ||
199 | |||
200 | // const secondWorkspace = workspacesForUser.find( | ||
201 | // w => w.name === 'workspace2', | ||
202 | // ); | ||
203 | // if (secondWorkspace?.services) { | ||
204 | // assert.deepEqual(JSON.parse(secondWorkspace.services), [ | ||
205 | // firstServiceUuid, | ||
206 | // secondServiceUuid, | ||
207 | // ]); | ||
208 | // } | ||
209 | |||
210 | // const thirdWorkspace = workspacesForUser.find( | ||
211 | // w => w.name === 'workspace3', | ||
212 | // ); | ||
213 | // if (thirdWorkspace?.services) { | ||
214 | // assert.deepEqual(JSON.parse(thirdWorkspace.services), [ | ||
215 | // firstServiceUuid, | ||
216 | // secondServiceUuid, | ||
217 | // thirdServiceUUid, | ||
218 | // ]); | ||
219 | // } | ||
220 | // } | ||
221 | // }); | ||
222 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | |||
3 | test.group('health page', () => { | ||
4 | test('returns a 200 response', async ({ client }) => { | ||
5 | const response = await client.get('/health'); | ||
6 | |||
7 | response.assertStatus(200); | ||
8 | response.assertBodyContains({ | ||
9 | api: 'success', | ||
10 | db: 'success', | ||
11 | }); | ||
12 | }); | ||
13 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | |||
3 | test.group('home page', () => { | ||
4 | test('returns a 200 response', async ({ client }) => { | ||
5 | const response = await client.get('/'); | ||
6 | |||
7 | response.assertStatus(200); | ||
8 | response.assertTextIncludes('Go to account dashboard'); | ||
9 | }); | ||
10 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | |||
3 | test.group('privacy page', () => { | ||
4 | test('returns a 200 response', async ({ client }) => { | ||
5 | const response = await client.get('/privacy'); | ||
6 | |||
7 | response.assertStatus(200); | ||
8 | response.assertTextIncludes('PRIVACY POLICY'); | ||
9 | }); | ||
10 | }); | ||
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 @@ | |||
1 | import { test } from '@japa/runner'; | ||
2 | |||
3 | test.group('terms page', () => { | ||
4 | test('returns a 200 response', async ({ client }) => { | ||
5 | const response = await client.get('/terms'); | ||
6 | |||
7 | response.assertStatus(200); | ||
8 | response.assertTextIncludes('Terms of Service'); | ||
9 | }); | ||
10 | }); | ||