@appsemble/e2e 0.35.11-test.4 → 0.35.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -14
- package/fixtures/index.d.ts +2 -2
- package/fixtures/index.js +2 -2
- package/fixtures/test/app/index.js +1 -14
- package/fixtures/test/app-collection/index.js +1 -11
- package/fixtures/test/index.d.ts +12 -1
- package/fixtures/test/index.js +52 -2
- package/fixtures/test/live-app/index.js +0 -3
- package/fixtures/test/organization/index.js +1 -8
- package/fixtures/test/resource/index.js +1 -8
- package/fixtures/test/training/index.js +2 -10
- package/index.d.ts +2 -2
- package/index.js +2 -2
- package/package.json +7 -7
- package/playwright.config.js +19 -11
- package/setup/create-accounts.setup.js +11 -0
- package/setup/teardown-accounts.setup.d.ts +1 -0
- package/setup/teardown-accounts.setup.js +11 -0
- package/tests/demo-apps/empty.spec.js +1 -1
- package/tests/demo-apps/holidays.spec.js +1 -1
- package/tests/demo-apps/notes.spec.js +1 -1
- package/tests/demo-apps/person.spec.js +1 -1
- package/tests/static-ui/apps.spec.js +10 -5
- package/tests/static-ui/blocks.spec.js +1 -1
- package/tests/static-ui/docs.spec.js +1 -1
- package/tests/static-ui/individual-app.spec.js +8 -5
- package/tests/static-ui/organizations.spec.js +1 -1
- package/tests/static-ui/training.spec.js +1 -1
- package/tests/studio/app-collections/app-collections.spec.js +1 -1
- package/tests/studio/individual-app.spec.js +1 -1
- package/tests/studio/logins/appsemble.spec.js +1 -1
- package/tests/studio/logins/demo.spec.js +1 -1
- package/tests/studio/logins/invite.spec.js +1 -1
- package/tests/studio/logins/oauth2.spec.js +1 -1
- package/tests/studio/logins/password.spec.js +1 -1
- package/tests/studio/logins/register.spec.js +1 -1
- package/tests/studio/logins/saml.spec.js +1 -1
- package/tests/studio/organizations.spec.js +1 -1
- package/tests/studio/payments/subscription-flow.spec.js +25 -14
- package/tests/studio/root.spec.js +1 -1
- package/tests/studio/trainings.spec.js +1 -3
- package/setup/auth.setup.js +0 -10
- /package/setup/{auth.setup.d.ts → create-accounts.setup.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#  Appsemble End 2 End Tests
|
|
2
2
|
|
|
3
3
|
> Run end 2 end tests on an Appsemble environment and provide Appsemble fixtures
|
|
4
4
|
|
|
5
|
-
[](https://gitlab.com/appsemble/appsemble/-/releases/0.35.14)
|
|
6
6
|
[](https://prettier.io)
|
|
7
7
|
|
|
8
8
|
## Table of Contents
|
|
@@ -38,6 +38,15 @@ test('should navigate to app', async ({ visitApp }) => {
|
|
|
38
38
|
});
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
## Development
|
|
42
|
+
|
|
43
|
+
This package depends on other packages in the Appsemble monorepo. To make sure all tests run
|
|
44
|
+
normally, go to each Appsemble dependency and run the following command:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
npm run prepack
|
|
48
|
+
```
|
|
49
|
+
|
|
41
50
|
## Appsemble end-to-end tests
|
|
42
51
|
|
|
43
52
|
To run end to end tests, run:
|
|
@@ -58,9 +67,6 @@ during the test as if you ran it using `ui mode`.
|
|
|
58
67
|
|
|
59
68
|
The end to end tests use the following environment variables:
|
|
60
69
|
|
|
61
|
-
- `BOT_ACCOUNT_EMAIL` — The email address used to login.
|
|
62
|
-
- `BOT_ACCOUNT_PASSWORD` — The password used to login.
|
|
63
|
-
- `ACCESS_TOKEN` — The access token of the account that's logged in.
|
|
64
70
|
- `CI` — Whether the tests are run in CI.
|
|
65
71
|
- `CI_MERGE_REQUEST_IID` — The id of the merge request if it's present.
|
|
66
72
|
- `APPSEMBLE_REVIEW_DOMAIN` — The review appsemble domain to use.
|
|
@@ -71,16 +77,21 @@ are used.
|
|
|
71
77
|
|
|
72
78
|
## Authentication
|
|
73
79
|
|
|
74
|
-
Some fixtures call the Appsemble API using the Playwright `request` object. This expects
|
|
75
|
-
set
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
`auth.setup.ts`
|
|
80
|
+
Some fixtures call the Appsemble API using the Playwright `request` object. This expects the worker
|
|
81
|
+
to have logged in and set the access token in the `request` fixture beforehand. This can look like
|
|
82
|
+
so:
|
|
79
83
|
|
|
80
84
|
```ts
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
85
|
+
export const test = base.extend<{}>({
|
|
86
|
+
async request({}, use) {
|
|
87
|
+
const newRequest = await request.newContext({
|
|
88
|
+
extraHTTPHeaders: {
|
|
89
|
+
Authorization: `Bearer ${accessToken}`,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
await use(newRequest);
|
|
93
|
+
await newRequest.dispose();
|
|
94
|
+
},
|
|
84
95
|
});
|
|
85
96
|
```
|
|
86
97
|
|
|
@@ -91,5 +102,5 @@ When writing end-to-end tests, have a look at the
|
|
|
91
102
|
|
|
92
103
|
## License
|
|
93
104
|
|
|
94
|
-
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.35.
|
|
105
|
+
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.35.14/LICENSE.md) ©
|
|
95
106
|
[Appsemble](https://appsemble.com)
|
package/fixtures/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * from './expect/index.js';
|
|
2
|
+
export * from './test/index.js';
|
package/fixtures/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * from './expect/index.js';
|
|
2
|
+
export * from './test/index.js';
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -18,9 +18,6 @@ export const test = base.extend({
|
|
|
18
18
|
}
|
|
19
19
|
const response = await request.post('/api/apps', {
|
|
20
20
|
multipart: formData,
|
|
21
|
-
headers: {
|
|
22
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
23
|
-
},
|
|
24
21
|
});
|
|
25
22
|
expect(response.status()).toBe(201);
|
|
26
23
|
const app = (await response.json());
|
|
@@ -41,9 +38,6 @@ export const test = base.extend({
|
|
|
41
38
|
}
|
|
42
39
|
const response = await request.patch(`/api/apps/${appId}`, {
|
|
43
40
|
multipart: formData,
|
|
44
|
-
headers: {
|
|
45
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
46
|
-
},
|
|
47
41
|
});
|
|
48
42
|
expect(response.status()).toBe(200);
|
|
49
43
|
const app = (await response.json());
|
|
@@ -53,11 +47,7 @@ export const test = base.extend({
|
|
|
53
47
|
},
|
|
54
48
|
async deleteApp({ request }, use) {
|
|
55
49
|
await use(async (appId) => {
|
|
56
|
-
const response = await request.delete(`/api/apps/${appId}
|
|
57
|
-
headers: {
|
|
58
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
59
|
-
},
|
|
60
|
-
});
|
|
50
|
+
const response = await request.delete(`/api/apps/${appId}`);
|
|
61
51
|
expect(response.status()).toBe(204);
|
|
62
52
|
});
|
|
63
53
|
},
|
|
@@ -86,9 +76,6 @@ export const test = base.extend({
|
|
|
86
76
|
};
|
|
87
77
|
const response = await request.post(`/api/apps/${appId}/messages`, {
|
|
88
78
|
data: appMessages,
|
|
89
|
-
headers: {
|
|
90
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
91
|
-
},
|
|
92
79
|
});
|
|
93
80
|
expect(response.status()).toBe(201);
|
|
94
81
|
return (await response.json());
|
|
@@ -20,9 +20,6 @@ export const test = base.extend({
|
|
|
20
20
|
}
|
|
21
21
|
const response = await request.post(`/api/organizations/${organizationId}/app-collections`, {
|
|
22
22
|
multipart: formData,
|
|
23
|
-
headers: {
|
|
24
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
25
|
-
},
|
|
26
23
|
});
|
|
27
24
|
expect(response.status()).toBe(201);
|
|
28
25
|
const appCollection = (await response.json());
|
|
@@ -31,11 +28,7 @@ export const test = base.extend({
|
|
|
31
28
|
},
|
|
32
29
|
async deleteAppCollection({ request }, use) {
|
|
33
30
|
await use(async (collectionId) => {
|
|
34
|
-
const response = await request.delete(`/api/app-collections/${collectionId}
|
|
35
|
-
headers: {
|
|
36
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
37
|
-
},
|
|
38
|
-
});
|
|
31
|
+
const response = await request.delete(`/api/app-collections/${collectionId}`);
|
|
39
32
|
expect(response.status()).toBe(204);
|
|
40
33
|
});
|
|
41
34
|
},
|
|
@@ -45,9 +38,6 @@ export const test = base.extend({
|
|
|
45
38
|
data: {
|
|
46
39
|
AppId: appId,
|
|
47
40
|
},
|
|
48
|
-
headers: {
|
|
49
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
50
|
-
},
|
|
51
41
|
});
|
|
52
42
|
expect(response.status()).toBe(204);
|
|
53
43
|
});
|
package/fixtures/test/index.d.ts
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Base test object with all utility fixtures
|
|
3
|
+
*/
|
|
4
|
+
export declare const baseTest: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & import("./app/index.js").AppFixtures & import("./app-collection/index.js").AppCollectionFixtures & import("./demo-app/index.js").DemoAppFixtures & import("./group/index.js").GroupFixtures & import("./live-app/index.js").LiveAppFixtures & import("./organization/index.js").OrganizationFixtures & import("./resource/index.js").ResourceFixtures & import("./studio/index.js").StudioFixtures & import("./test-utils/index.js").TestUtilsFixtures & import("./training/index.js").TrainingFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
|
5
|
+
/**
|
|
6
|
+
* Test object with the worker's storage state saved
|
|
7
|
+
*
|
|
8
|
+
* Used for tests that require a logged in user
|
|
9
|
+
*/
|
|
10
|
+
export declare const authenticatedTest: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & import("./app/index.js").AppFixtures & import("./app-collection/index.js").AppCollectionFixtures & import("./demo-app/index.js").DemoAppFixtures & import("./group/index.js").GroupFixtures & import("./live-app/index.js").LiveAppFixtures & import("./organization/index.js").OrganizationFixtures & import("./resource/index.js").ResourceFixtures & import("./studio/index.js").StudioFixtures & import("./test-utils/index.js").TestUtilsFixtures & import("./training/index.js").TrainingFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions & {
|
|
11
|
+
workerStorageState: string;
|
|
12
|
+
}>;
|
package/fixtures/test/index.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { expect, mergeTests, request } from '@playwright/test';
|
|
2
5
|
import { test as appFixtures } from './app/index.js';
|
|
3
6
|
import { test as appCollectionFixtures } from './app-collection/index.js';
|
|
4
7
|
import { test as demoAppFixtures } from './demo-app/index.js';
|
|
@@ -9,5 +12,52 @@ import { test as resourceFixtures } from './resource/index.js';
|
|
|
9
12
|
import { test as studioFixtures } from './studio/index.js';
|
|
10
13
|
import { test as testUtilsFixtures } from './test-utils/index.js';
|
|
11
14
|
import { test as trainingFixtures } from './training/index.js';
|
|
12
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Base test object with all utility fixtures
|
|
17
|
+
*/
|
|
18
|
+
export const baseTest = mergeTests(appFixtures, appCollectionFixtures, demoAppFixtures, groupFixtures, liveAppFixtures, organizationFixtures, resourceFixtures, studioFixtures, testUtilsFixtures, trainingFixtures);
|
|
19
|
+
/**
|
|
20
|
+
* Test object with the worker's storage state saved
|
|
21
|
+
*
|
|
22
|
+
* Used for tests that require a logged in user
|
|
23
|
+
*/
|
|
24
|
+
export const authenticatedTest = baseTest.extend({
|
|
25
|
+
// Use the same storage state for all tests in this worker.
|
|
26
|
+
storageState: ({ workerStorageState }, use) => use(workerStorageState),
|
|
27
|
+
workerStorageState: [
|
|
28
|
+
async ({ browser }, use, testInfo) => {
|
|
29
|
+
const id = `worker-${testInfo.parallelIndex}`;
|
|
30
|
+
const email = `${id}@appsemble.com`;
|
|
31
|
+
const password = id;
|
|
32
|
+
const fileName = resolve(testInfo.project.outputDir, `.auth/${id}.json`);
|
|
33
|
+
if (existsSync(fileName)) {
|
|
34
|
+
await use(fileName);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const page = await browser.newPage({ storageState: undefined });
|
|
38
|
+
await page.goto(`${testInfo.project.use.baseURL}/en/login`);
|
|
39
|
+
await page.getByTestId('email').fill(email);
|
|
40
|
+
await page.getByTestId('password').fill(password);
|
|
41
|
+
await page.getByTestId('login').click();
|
|
42
|
+
// Wait until auth state is saved in browser storage
|
|
43
|
+
await expect(page.getByText('My Apps')).toBeVisible();
|
|
44
|
+
await page.context().storageState({ path: fileName });
|
|
45
|
+
await page.close();
|
|
46
|
+
await use(fileName);
|
|
47
|
+
},
|
|
48
|
+
{ scope: 'worker' },
|
|
49
|
+
],
|
|
50
|
+
async request({ baseURL, storageState }, use) {
|
|
51
|
+
const accessToken = JSON.parse(await readFile(String(storageState), 'utf8')).origins[0]
|
|
52
|
+
.localStorage[1].value;
|
|
53
|
+
const newRequest = await request.newContext({
|
|
54
|
+
baseURL,
|
|
55
|
+
extraHTTPHeaders: {
|
|
56
|
+
Authorization: `Bearer ${accessToken}`,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
await use(newRequest);
|
|
60
|
+
await newRequest.dispose();
|
|
61
|
+
},
|
|
62
|
+
});
|
|
13
63
|
//# sourceMappingURL=index.js.map
|
|
@@ -13,9 +13,6 @@ export const test = base.extend({
|
|
|
13
13
|
formData.set('icon', icon ?? null);
|
|
14
14
|
const response = await request.post('/api/organizations', {
|
|
15
15
|
multipart: formData,
|
|
16
|
-
headers: {
|
|
17
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
18
|
-
},
|
|
19
16
|
});
|
|
20
17
|
expect(response.status()).toBe(201);
|
|
21
18
|
return response.json();
|
|
@@ -23,11 +20,7 @@ export const test = base.extend({
|
|
|
23
20
|
},
|
|
24
21
|
async deleteOrganization({ request }, use) {
|
|
25
22
|
await use(async (id) => {
|
|
26
|
-
const response = await request.delete(`/api/organizations/${id}
|
|
27
|
-
headers: {
|
|
28
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
29
|
-
},
|
|
30
|
-
});
|
|
23
|
+
const response = await request.delete(`/api/organizations/${id}`);
|
|
31
24
|
expect(response.status()).toBe(200);
|
|
32
25
|
});
|
|
33
26
|
},
|
|
@@ -12,9 +12,6 @@ export const test = base.extend({
|
|
|
12
12
|
}
|
|
13
13
|
const response = await request.post(`/api/apps/${appId}/resources/${type}`, {
|
|
14
14
|
multipart: formData,
|
|
15
|
-
headers: {
|
|
16
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
17
|
-
},
|
|
18
15
|
});
|
|
19
16
|
expect(response.status()).toBe(201);
|
|
20
17
|
const resource = (await response.json());
|
|
@@ -24,11 +21,7 @@ export const test = base.extend({
|
|
|
24
21
|
},
|
|
25
22
|
async deleteAllResources({ request }, use) {
|
|
26
23
|
await use(async (appId) => {
|
|
27
|
-
const response = await request.delete(`/api/apps/${appId}/resources
|
|
28
|
-
headers: {
|
|
29
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
30
|
-
},
|
|
31
|
-
});
|
|
24
|
+
const response = await request.delete(`/api/apps/${appId}/resources`);
|
|
32
25
|
expect(response.status()).toBe(204);
|
|
33
26
|
});
|
|
34
27
|
},
|
|
@@ -2,21 +2,13 @@ import { test as base, expect } from '@playwright/test';
|
|
|
2
2
|
export const test = base.extend({
|
|
3
3
|
async resetTrainingProgress({ request }, use) {
|
|
4
4
|
await use(async () => {
|
|
5
|
-
const response = await request.delete('/api/trainings/completed'
|
|
6
|
-
headers: {
|
|
7
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
8
|
-
},
|
|
9
|
-
});
|
|
5
|
+
const response = await request.delete('/api/trainings/completed');
|
|
10
6
|
expect(response.status()).toBe(204);
|
|
11
7
|
});
|
|
12
8
|
},
|
|
13
9
|
async completeTraining({ request }, use) {
|
|
14
10
|
await use(async (trainingId) => {
|
|
15
|
-
const response = await request.post(`/api/trainings/completed/${trainingId}
|
|
16
|
-
headers: {
|
|
17
|
-
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
|
|
18
|
-
},
|
|
19
|
-
});
|
|
11
|
+
const response = await request.post(`/api/trainings/completed/${trainingId}`);
|
|
20
12
|
expect(response.status()).toBe(201);
|
|
21
13
|
});
|
|
22
14
|
},
|
package/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * from './fixtures/expect/index.js';
|
|
2
|
+
export * from './fixtures/test/index.js';
|
package/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * from './fixtures/expect/index.js';
|
|
2
|
+
export * from './fixtures/test/index.js';
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/e2e",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.14",
|
|
4
4
|
"description": "End-to-end tests used for testing Appsemble apps in an existing environment.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -38,16 +38,16 @@
|
|
|
38
38
|
"test": "vitest"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@appsemble/
|
|
42
|
-
"@appsemble/
|
|
43
|
-
"@appsemble/
|
|
44
|
-
"jwt-decode": "^4.0.0",
|
|
45
|
-
"@fortawesome/fontawesome-free": "^6.0.0",
|
|
41
|
+
"@appsemble/lang-sdk": "0.35.14",
|
|
42
|
+
"@appsemble/node-utils": "0.35.14",
|
|
43
|
+
"@appsemble/types": "0.35.14",
|
|
46
44
|
"@playwright/test": "1.51.1",
|
|
47
|
-
"bulma": "=0.9.3",
|
|
48
45
|
"strip-indent": "^4.0.0"
|
|
49
46
|
},
|
|
50
47
|
"engines": {
|
|
51
48
|
"node": ">=20"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"eslint-plugin-playwright": "2.2.2"
|
|
52
52
|
}
|
|
53
53
|
}
|
package/playwright.config.js
CHANGED
|
@@ -5,14 +5,14 @@ const { APPSEMBLE_REVIEW_DOMAIN, APPSEMBLE_STAGING_DOMAIN, CI, CI_MERGE_REQUEST_
|
|
|
5
5
|
*/
|
|
6
6
|
export default defineConfig({
|
|
7
7
|
testMatch: '**/*.spec.ts',
|
|
8
|
-
//
|
|
9
|
-
fullyParallel:
|
|
8
|
+
// Run tests in parallel
|
|
9
|
+
fullyParallel: true,
|
|
10
10
|
// Fail the build on CI if you accidentally left test.only in the source code.
|
|
11
11
|
forbidOnly: Boolean(CI),
|
|
12
12
|
// Retry on CI only
|
|
13
13
|
retries: CI ? 2 : 0,
|
|
14
|
-
//
|
|
15
|
-
workers: 1,
|
|
14
|
+
// Amount of workers to run tests with
|
|
15
|
+
workers: CI ? 2 : 1,
|
|
16
16
|
// Reporter to use. See https://playwright.dev/docs/test-reporters
|
|
17
17
|
reporter: [['junit', { outputFile: 'results.xml' }]],
|
|
18
18
|
// Prevent the pipeline from timing out
|
|
@@ -50,22 +50,30 @@ export default defineConfig({
|
|
|
50
50
|
use: { ...devices['Desktop Safari'] },
|
|
51
51
|
fullyParallel: true,
|
|
52
52
|
},
|
|
53
|
-
{
|
|
53
|
+
{
|
|
54
|
+
name: 'create-accounts',
|
|
55
|
+
testMatch: /create-accounts\.setup\.ts/,
|
|
56
|
+
teardown: 'teardown-accounts',
|
|
57
|
+
},
|
|
54
58
|
{
|
|
55
59
|
name: 'chromium',
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
testIgnore: '**/static-ui/**',
|
|
61
|
+
use: { ...devices['Desktop Chrome'] },
|
|
62
|
+
dependencies: ['create-accounts'],
|
|
58
63
|
},
|
|
59
64
|
{
|
|
60
65
|
name: 'firefox',
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
testIgnore: '**/static-ui/**',
|
|
67
|
+
use: { ...devices['Desktop Firefox'] },
|
|
68
|
+
dependencies: ['create-accounts'],
|
|
63
69
|
},
|
|
64
70
|
{
|
|
65
71
|
name: 'webkit',
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
testIgnore: '**/static-ui/**',
|
|
73
|
+
use: { ...devices['Desktop Safari'] },
|
|
74
|
+
dependencies: ['create-accounts'],
|
|
68
75
|
},
|
|
76
|
+
{ name: 'teardown-accounts', testMatch: /teardown-accounts\.setup\.ts/ },
|
|
69
77
|
],
|
|
70
78
|
});
|
|
71
79
|
//# sourceMappingURL=playwright.config.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createUser } from '@appsemble/node-utils';
|
|
2
|
+
import { test as setup } from '@playwright/test';
|
|
3
|
+
setup('create accounts for workers', async ({}, testInfo) => {
|
|
4
|
+
for (let i = 0; i < testInfo.config.workers; i += 1) {
|
|
5
|
+
const id = `worker-${i}`;
|
|
6
|
+
const email = `${id}@appsemble.com`;
|
|
7
|
+
const password = id;
|
|
8
|
+
await createUser(id, email, password);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=create-accounts.setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { rm } from 'node:fs/promises';
|
|
2
|
+
import { deleteUser } from '@appsemble/node-utils';
|
|
3
|
+
import { test as setup } from '@playwright/test';
|
|
4
|
+
setup('delete worker accounts', async ({}, testInfo) => {
|
|
5
|
+
for (let i = 0; i < testInfo.config.workers; i += 1) {
|
|
6
|
+
const email = `worker-${i}@appsemble.com`;
|
|
7
|
+
await deleteUser(email);
|
|
8
|
+
}
|
|
9
|
+
await rm(`${testInfo.project.outputDir}/.auth`, { recursive: true, force: true });
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=teardown-accounts.setup.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { createUser, deleteUser } from '@appsemble/node-utils';
|
|
2
|
+
import { expect, baseTest as test } from '../../index.js';
|
|
3
3
|
test.describe('Apps', () => {
|
|
4
4
|
// Prevent worker from being logged in automatically
|
|
5
5
|
test.use({ storageState: { cookies: [], origins: [] } });
|
|
@@ -15,10 +15,15 @@ test.describe('Apps', () => {
|
|
|
15
15
|
test('should show the page header', async ({ page }) => {
|
|
16
16
|
await expect(page.getByRole('heading', { name: 'All Apps', exact: true })).toBeVisible();
|
|
17
17
|
});
|
|
18
|
-
test('should display “My Apps” when logged in', async ({
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
test('should display “My Apps” when logged in', async ({ browser, loginUserOnPage, randomTestId, }) => {
|
|
19
|
+
const name = 'Test user';
|
|
20
|
+
const email = `${randomTestId()}@test.com`;
|
|
21
|
+
const password = '1';
|
|
22
|
+
await createUser(name, email, password);
|
|
23
|
+
const page = await browser.newPage({ storageState: undefined });
|
|
24
|
+
await loginUserOnPage(email, password, page);
|
|
21
25
|
await expect(page.getByRole('heading', { name: 'My Apps', exact: true })).toBeVisible();
|
|
26
|
+
await deleteUser(email);
|
|
22
27
|
});
|
|
23
28
|
test('should render list of apps', async ({ page }) => {
|
|
24
29
|
await expect(page.getByRole('link', { name: 'Test app Test org' }).first())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AppPermission, predefinedAppRoles } from '@appsemble/lang-sdk';
|
|
2
2
|
import { OrganizationPermission, predefinedOrganizationRoles } from '@appsemble/types';
|
|
3
|
-
import { expect, test } from '../../index.js';
|
|
3
|
+
import { expect, baseTest as test } from '../../index.js';
|
|
4
4
|
test.describe('Docs', () => {
|
|
5
5
|
test('should render the index page', async ({ page }) => {
|
|
6
6
|
await page.goto('/en/docs');
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { createUser } from '@appsemble/node-utils';
|
|
2
|
+
import { expect, baseTest as test } from '../../index.js';
|
|
3
3
|
let page;
|
|
4
4
|
test.describe('Individual app', () => {
|
|
5
|
-
test.beforeAll(async ({ browser, loginUserOnPage }) => {
|
|
5
|
+
test.beforeAll(async ({ browser, loginUserOnPage, randomTestId }) => {
|
|
6
|
+
const name = 'Test user';
|
|
7
|
+
const email = `${randomTestId()}@test.com`;
|
|
8
|
+
const password = '1';
|
|
9
|
+
await createUser(name, email, password);
|
|
6
10
|
page = await browser.newPage({ storageState: undefined });
|
|
7
|
-
|
|
8
|
-
await loginUserOnPage(BOT_ACCOUNT_EMAIL, BOT_ACCOUNT_PASSWORD, page);
|
|
11
|
+
await loginUserOnPage(email, password, page);
|
|
9
12
|
});
|
|
10
13
|
test.afterAll(async () => {
|
|
11
14
|
await page.close();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, test } from '../../fixtures/index.js';
|
|
1
|
+
import { expect, baseTest as test } from '../../fixtures/index.js';
|
|
2
2
|
test.describe('Training', () => {
|
|
3
3
|
// Prevent worker from being logged in automatically
|
|
4
4
|
test.use({ storageState: { cookies: [], origins: [] } });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { expect, test } from '../../index.js';
|
|
2
|
+
import { expect, authenticatedTest as test } from '../../index.js';
|
|
3
3
|
let appId;
|
|
4
4
|
test.describe('Individual app', () => {
|
|
5
5
|
test.beforeAll(async ({ createApp, createOrganization, randomTestId }) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, test } from '../../../index.js';
|
|
1
|
+
import { expect, authenticatedTest as test } from '../../../index.js';
|
|
2
2
|
test.describe('Appsemble OAuth2 app login', () => {
|
|
3
3
|
test.fixme('should work', async ({ page }) => {
|
|
4
4
|
await page.getByRole('link', { name: 'Login' }).click();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, test } from '../../../index.js';
|
|
1
|
+
import { expect, authenticatedTest as test } from '../../../index.js';
|
|
2
2
|
test.describe('App invite', () => {
|
|
3
3
|
test.fixme('should work', async ({ page }) => {
|
|
4
4
|
await expect(page.getByRole('heading', { name: 'Example Page A' })).toBeVisible();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, test } from '../../../index.js';
|
|
1
|
+
import { expect, authenticatedTest as test } from '../../../index.js';
|
|
2
2
|
test.describe('App password login', () => {
|
|
3
3
|
test.fixme('should work', async ({ page }) => {
|
|
4
4
|
await page.getByRole('link', { name: 'Login' }).click();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, test } from '../../../index.js';
|
|
1
|
+
import { expect, authenticatedTest as test } from '../../../index.js';
|
|
2
2
|
test.describe('App registration', () => {
|
|
3
3
|
test.fixme('should work', async ({ page }) => {
|
|
4
4
|
await page.getByRole('link', { name: 'Login' }).click();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { expect, test } from '../../../index.js';
|
|
1
|
+
import { expect, authenticatedTest as test } from '../../../index.js';
|
|
2
2
|
test.describe('App SAML login', () => {
|
|
3
3
|
test.fixme('should work', async ({ page }) => {
|
|
4
4
|
await page.getByRole('link', { name: 'Login' }).click();
|
|
@@ -1,15 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { updateSubscription } from '@appsemble/node-utils';
|
|
2
|
+
import { SubscriptionPlanType } from '@appsemble/types';
|
|
3
|
+
import { expect, authenticatedTest as test } from '../../../fixtures/index.js';
|
|
4
|
+
let organizationId;
|
|
3
5
|
const email = 'test+test_mode@test.com';
|
|
4
6
|
const streetName = 'street one';
|
|
5
7
|
const houseNumber = '12a';
|
|
6
8
|
const city = 'Eindhoven';
|
|
7
9
|
const zipCode = '1234DF';
|
|
8
10
|
const country = 'NL';
|
|
11
|
+
const { CI } = process.env;
|
|
9
12
|
test.describe('Payments', () => {
|
|
10
|
-
|
|
13
|
+
test.beforeAll(async ({ createOrganization, randomTestId }) => {
|
|
14
|
+
organizationId = (await createOrganization({ id: randomTestId() })).id;
|
|
15
|
+
});
|
|
16
|
+
test.beforeEach(async () => {
|
|
17
|
+
await updateSubscription(organizationId, SubscriptionPlanType.Standard);
|
|
18
|
+
});
|
|
19
|
+
test.afterAll(async ({ deleteOrganization }) => {
|
|
20
|
+
await deleteOrganization(organizationId);
|
|
21
|
+
});
|
|
11
22
|
test('should cancel a subscription', async ({ page }) => {
|
|
12
|
-
await page.goto(`/en/organizations/${
|
|
23
|
+
await page.goto(`/en/organizations/${organizationId}/subscriptions`);
|
|
13
24
|
await page
|
|
14
25
|
.locator('.card:has-text("STANDARD")')
|
|
15
26
|
.getByRole('button', { name: 'Cancel' })
|
|
@@ -24,15 +35,15 @@ test.describe('Payments', () => {
|
|
|
24
35
|
}
|
|
25
36
|
});
|
|
26
37
|
(CI ? test.skip : test)('should confirm correct redirect by extension', async ({ page }) => {
|
|
27
|
-
await page.goto(`/en/organizations/${
|
|
38
|
+
await page.goto(`/en/organizations/${organizationId}/subscriptions`);
|
|
28
39
|
await page.locator('.card:has-text("STANDARD")').getByRole('link', { name: 'Extend' }).click();
|
|
29
|
-
await expect(page).toHaveTitle('Activate · Appsemble'
|
|
40
|
+
await expect(page).toHaveTitle('Activate · Appsemble');
|
|
30
41
|
});
|
|
31
42
|
test('should purchase a subscription', async ({ page }) => {
|
|
32
|
-
await page.goto(`/en/organizations/${
|
|
43
|
+
await page.goto(`/en/organizations/${organizationId}/subscriptions`);
|
|
33
44
|
await page.locator('.card:has-text("EXTENSIVE")').getByRole('link', { name: 'Switch' }).click();
|
|
34
45
|
await expect(page.getByText('Select type of')).toBeVisible();
|
|
35
|
-
await expect(page.getByText('Total subscription
|
|
46
|
+
await expect(page.getByText('Total subscription price')).toBeVisible();
|
|
36
47
|
await page.getByRole('button', { name: 'Checkout ' }).click();
|
|
37
48
|
await page.getByLabel('Email').fill(email);
|
|
38
49
|
await page.getByLabel('Street name').fill(streetName);
|
|
@@ -40,23 +51,23 @@ test.describe('Payments', () => {
|
|
|
40
51
|
await page.getByLabel('City').fill(city);
|
|
41
52
|
await page.getByLabel('Zip code').fill(zipCode);
|
|
42
53
|
await page.getByLabel('Country').selectOption(country);
|
|
43
|
-
await page.getByLabel('Name', { exact: true }).fill(
|
|
54
|
+
await page.getByLabel('Name', { exact: true }).fill(organizationId);
|
|
44
55
|
await page.getByRole('button').getByText('Continue').click();
|
|
45
|
-
await expect(page.getByText('Total subscription
|
|
56
|
+
await expect(page.getByText('Total subscription price')).toBeVisible();
|
|
46
57
|
// eslint-disable-next-line playwright/no-conditional-in-test
|
|
47
58
|
if (!CI) {
|
|
48
59
|
await page.getByRole('button', { name: 'Summary ' }).click();
|
|
49
|
-
await expect(page).toHaveTitle('Appsemble b.v.'
|
|
60
|
+
await expect(page).toHaveTitle('Appsemble b.v.');
|
|
50
61
|
await page.getByTestId('sepa_debit-accordion-item').click();
|
|
51
62
|
await page.locator('[placeholder="\\NL00 AAAA 0000 0000 00"]').click();
|
|
52
63
|
await page.locator('[placeholder="\\NL00 AAAA 0000 0000 00"]').fill('NL39RABO0300065264');
|
|
53
|
-
await page.getByLabel('Name on account').fill(
|
|
64
|
+
await page.getByLabel('Name on account').fill(organizationId);
|
|
54
65
|
await page.getByPlaceholder('Address line 1').fill(streetName);
|
|
55
66
|
await page.getByPlaceholder('Postal code').fill(zipCode);
|
|
56
67
|
await page.getByPlaceholder('City').fill(city);
|
|
57
68
|
await page.getByTestId('hosted-payment-submit-button').click();
|
|
58
|
-
await expect(page.getByText('Purchase successful')).toBeVisible(
|
|
59
|
-
await expect(page.getByText('New expiration date')).toBeVisible(
|
|
69
|
+
await expect(page.getByText('Purchase successful')).toBeVisible();
|
|
70
|
+
await expect(page.getByText('New expiration date')).toBeVisible();
|
|
60
71
|
await expect(page.getByText('Subscription plan')).toBeVisible();
|
|
61
72
|
await expect(page.getByText('Renewal period')).toBeVisible();
|
|
62
73
|
await expect(page.getByText('extensive')).toBeVisible();
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { expect, test } from '../../index.js';
|
|
1
|
+
import { expect, authenticatedTest as test } from '../../index.js';
|
|
2
2
|
test.describe('Training', () => {
|
|
3
3
|
test.beforeEach(async ({ page, resetTrainingProgress }) => {
|
|
4
4
|
await resetTrainingProgress();
|
|
5
5
|
await page.goto('/en/trainings');
|
|
6
6
|
});
|
|
7
|
-
// We only have 1 account, so tests can influence each other
|
|
8
|
-
test.describe.configure({ mode: 'serial' });
|
|
9
7
|
test("should show 'Training' in breadcrumbs", async ({ page }) => {
|
|
10
8
|
await expect(page.getByLabel('Breadcrumbs').getByRole('listitem').filter({ hasText: 'Training' })).toBeVisible();
|
|
11
9
|
});
|
package/setup/auth.setup.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { expect, test as setup } from '../index.js';
|
|
2
|
-
const { BOT_ACCOUNT_EMAIL, BOT_ACCOUNT_PASSWORD } = process.env;
|
|
3
|
-
setup('authenticate', async ({ loginUser }) => {
|
|
4
|
-
expect(BOT_ACCOUNT_EMAIL && BOT_ACCOUNT_PASSWORD).not.toBeUndefined();
|
|
5
|
-
const accessToken = await loginUser(BOT_ACCOUNT_EMAIL, BOT_ACCOUNT_PASSWORD, true);
|
|
6
|
-
expect(accessToken).toStrictEqual(expect.any(String));
|
|
7
|
-
// Playwright uses this environment variable to authenticate API requests
|
|
8
|
-
process.env.ACCESS_TOKEN = accessToken;
|
|
9
|
-
});
|
|
10
|
-
//# sourceMappingURL=auth.setup.js.map
|
|
File without changes
|