@live-change/user-frontend 0.9.203 → 0.9.205
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/.node-version +1 -0
- package/.nvmrc +1 -0
- package/dist/server/app.config.d.ts +177 -0
- package/dist/server/app.config.js +135 -0
- package/dist/server/init-functions.d.ts +6 -0
- package/dist/server/init-functions.js +20 -0
- package/dist/server/init.d.ts +1 -0
- package/dist/server/init.js +46 -0
- package/dist/server/security.config.d.ts +47 -0
- package/dist/server/security.config.js +48 -0
- package/dist/server/services.list.d.ts +21 -0
- package/dist/server/services.list.js +24 -0
- package/dist/server/start.d.ts +1 -0
- package/dist/server/start.js +32 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/e2e/connectEmailCode.test.ts +11 -7
- package/e2e/connectEmailLink.test.ts +12 -6
- package/e2e/delete.test.ts +6 -3
- package/e2e/disconnectEmail.test.ts +11 -12
- package/e2e/e2eSuite.ts +8 -12
- package/e2e/env.ts +56 -58
- package/e2e/execution-time-report.ts +7 -0
- package/e2e/resetPasswordWithEmailCode.test.ts +8 -6
- package/e2e/resetPasswordWithEmailLink.test.ts +8 -6
- package/e2e/runner.ts +10 -0
- package/e2e/setPassword.test.ts +19 -12
- package/e2e/signInEmailCode.test.ts +4 -2
- package/e2e/signInEmailLink.test.ts +5 -3
- package/e2e/signInEmailPassword.test.ts +3 -2
- package/e2e/signOut.test.ts +5 -2
- package/e2e/signUpEmailCode.test.ts +5 -2
- package/e2e/signUpEmailLink.test.ts +5 -3
- package/e2e/steps.ts +57 -21
- package/e2e/withBrowser.ts +2 -16
- package/front/src/NavBar.vue +1 -1
- package/front/src/connected/Connected.vue +2 -1
- package/front/src/message-auth/MessageLink.vue +5 -2
- package/front/src/message-auth/MessageSent.vue +2 -3
- package/front/src/message-auth/email/ConnectEmail.vue +1 -5
- package/front/src/message-auth/email/ResetPasswordEmail.vue +1 -5
- package/front/src/message-auth/email/SignInEmail.vue +1 -5
- package/front/src/message-auth/sms/ResetPasswordSms.vue +1 -5
- package/front/src/message-auth/sms/SignInSms.vue +1 -5
- package/front/src/message-auth/sms/SignUpSms.vue +1 -5
- package/front/src/password/ResetPasswordForm.vue +3 -1
- package/package.json +37 -29
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import test from '
|
|
1
|
+
import { e2eSuite, test } from './e2eSuite.js'
|
|
2
|
+
import { waitForHydration } from '@live-change/e2e-test'
|
|
2
3
|
import assert from 'node:assert'
|
|
3
4
|
import App from '@live-change/framework'
|
|
4
5
|
import randomProfile from 'random-profile-generator'
|
|
5
6
|
import { withBrowser } from './withBrowser.js'
|
|
6
|
-
import { useSecretCode
|
|
7
|
-
import { e2eSuite } from './e2eSuite.js'
|
|
7
|
+
import { useSecretCode } from './steps.js'
|
|
8
8
|
|
|
9
9
|
const app = App.app()
|
|
10
10
|
const email = randomProfile.profile().firstName.toLowerCase() + '@test.com'
|
|
@@ -23,6 +23,7 @@ e2eSuite('connectEmailCode', () => {
|
|
|
23
23
|
await User.create({ id: user, roles: [] })
|
|
24
24
|
await Email.create({ id: email, email, user })
|
|
25
25
|
await page.goto(env.url + '/', { waitUntil: 'networkidle' })
|
|
26
|
+
await waitForHydration(page)
|
|
26
27
|
const session = await page.evaluate(
|
|
27
28
|
() => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session
|
|
28
29
|
)
|
|
@@ -30,17 +31,19 @@ e2eSuite('connectEmailCode', () => {
|
|
|
30
31
|
|
|
31
32
|
// Reload so client gets user from server; otherwise router redirects to sign-in when opening /user/settings/connected
|
|
32
33
|
await page.reload({ waitUntil: 'networkidle' })
|
|
34
|
+
await waitForHydration(page)
|
|
33
35
|
await page.goto(env.url + '/user/settings/connected', { waitUntil: 'networkidle' })
|
|
36
|
+
await waitForHydration(page)
|
|
34
37
|
await page.getByText(email).waitFor({ state: 'visible', timeout: 15000 })
|
|
35
38
|
assert.strictEqual(await page.getByText(email2).isVisible(), false, 'email2 should not be visible')
|
|
36
39
|
|
|
37
|
-
await page.
|
|
38
|
-
|
|
40
|
+
await page.getByRole('link', { name: /add email/i }).first().click()
|
|
41
|
+
await page.waitForURL(/connect-email/, { timeout: 15000 })
|
|
39
42
|
|
|
40
43
|
if (!happyPath) {
|
|
41
44
|
await page.fill('input#email', email)
|
|
42
45
|
await page.click('button[type=submit]')
|
|
43
|
-
assert.ok(page.url().includes('
|
|
46
|
+
assert.ok(page.url().includes('connect-email'))
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
await page.fill('input#email', email2)
|
|
@@ -56,11 +59,12 @@ e2eSuite('connectEmailCode', () => {
|
|
|
56
59
|
assert.strictEqual((authenticationData as { messageData?: { user: string } })?.messageData?.user, user, 'authentication contains user')
|
|
57
60
|
|
|
58
61
|
await useSecretCode(page, env, authentication, happyPath)
|
|
59
|
-
await page.waitForURL('**/connect-finished', { timeout:
|
|
62
|
+
await page.waitForURL('**/connect-finished', { timeout: 30000 })
|
|
60
63
|
assert.ok(page.url().includes('/connect-finished'))
|
|
61
64
|
|
|
62
65
|
if (!happyPath) {
|
|
63
66
|
await page.goto(url, { waitUntil: 'networkidle' })
|
|
67
|
+
await waitForHydration(page)
|
|
64
68
|
assert.ok(page.url().includes('/connect-finished'))
|
|
65
69
|
}
|
|
66
70
|
})
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import test from '
|
|
1
|
+
import { e2eSuite, test } from './e2eSuite.js'
|
|
2
|
+
import { waitForHydration } from '@live-change/e2e-test'
|
|
2
3
|
import assert from 'node:assert'
|
|
3
4
|
import App from '@live-change/framework'
|
|
4
5
|
import randomProfile from 'random-profile-generator'
|
|
5
6
|
import { withBrowser } from './withBrowser.js'
|
|
6
7
|
import { useSecretLink } from './steps.js'
|
|
7
|
-
import { e2eSuite } from './e2eSuite.js'
|
|
8
8
|
|
|
9
9
|
const app = App.app()
|
|
10
10
|
const email = randomProfile.profile().firstName.toLowerCase() + '@test.com'
|
|
@@ -23,26 +23,31 @@ e2eSuite('connectEmailLink', () => {
|
|
|
23
23
|
await User.create({ id: user, roles: [] })
|
|
24
24
|
await Email.create({ id: email, email, user })
|
|
25
25
|
await page.goto(env.url + '/', { waitUntil: 'networkidle' })
|
|
26
|
+
await waitForHydration(page)
|
|
26
27
|
const session = await page.evaluate(
|
|
27
28
|
() => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session
|
|
28
29
|
)
|
|
29
30
|
await AuthenticatedUser.create({ id: session, user, session })
|
|
30
31
|
|
|
31
32
|
await page.reload({ waitUntil: 'networkidle' })
|
|
33
|
+
await waitForHydration(page)
|
|
32
34
|
await page.goto(env.url + '/user/settings/connected', { waitUntil: 'networkidle' })
|
|
35
|
+
await waitForHydration(page)
|
|
33
36
|
await page.getByText(email).waitFor({ state: 'visible', timeout: 15000 })
|
|
34
37
|
assert.strictEqual(await page.getByText(email2).isVisible(), false, 'email2 should not be visible')
|
|
35
|
-
|
|
36
|
-
await page.
|
|
37
|
-
|
|
38
|
+
|
|
39
|
+
await page.locator('[data-testid="connect-email"]').first().click()
|
|
40
|
+
await page.waitForURL(/connect-email/, { timeout: 15000 })
|
|
38
41
|
|
|
39
42
|
if (!happyPath) {
|
|
40
43
|
await page.fill('input#email', email)
|
|
41
44
|
await page.click('button[type=submit]')
|
|
45
|
+
assert.ok(page.url().includes('connect-email'))
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
await page.fill('input#email', email2)
|
|
45
49
|
await page.click('button[type=submit]')
|
|
50
|
+
await page.waitForURL('**/sent/*', { timeout: 10000 })
|
|
46
51
|
assert.ok(page.url().includes('/sent/'))
|
|
47
52
|
|
|
48
53
|
const url = page.url()
|
|
@@ -53,11 +58,12 @@ e2eSuite('connectEmailLink', () => {
|
|
|
53
58
|
assert.strictEqual((authenticationData as { messageData?: { user: string } })?.messageData?.user, user, 'authentication contains user')
|
|
54
59
|
|
|
55
60
|
const linkData = await useSecretLink(page, env, authentication, happyPath)
|
|
56
|
-
await page.waitForURL('**/connect-finished', { timeout:
|
|
61
|
+
await page.waitForURL('**/connect-finished', { timeout: 30000 })
|
|
57
62
|
assert.ok(page.url().includes('/connect-finished'))
|
|
58
63
|
|
|
59
64
|
if (!happyPath) {
|
|
60
65
|
await page.goto(env.url + '/user/link/' + linkData.secretCode, { waitUntil: 'networkidle' })
|
|
66
|
+
await waitForHydration(page)
|
|
61
67
|
await page.getByText('Link used').waitFor({ state: 'visible' })
|
|
62
68
|
}
|
|
63
69
|
})
|
package/e2e/delete.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import test from '
|
|
1
|
+
import { e2eSuite, test } from './e2eSuite.js'
|
|
2
|
+
import { waitForHydration } from '@live-change/e2e-test'
|
|
2
3
|
import assert from 'node:assert'
|
|
3
4
|
import App from '@live-change/framework'
|
|
4
5
|
import randomProfile from 'random-profile-generator'
|
|
5
6
|
import { withBrowser } from './withBrowser.js'
|
|
6
|
-
import { e2eSuite } from './e2eSuite.js'
|
|
7
7
|
|
|
8
8
|
const app = App.app()
|
|
9
9
|
const name = randomProfile.profile().firstName.toLowerCase()
|
|
@@ -21,19 +21,22 @@ e2eSuite('delete', () => {
|
|
|
21
21
|
await User.create({ id: user, roles: [] })
|
|
22
22
|
await Email.create({ id: email, email, user })
|
|
23
23
|
await page.goto(env.url + '/', { waitUntil: 'networkidle' })
|
|
24
|
+
await waitForHydration(page)
|
|
24
25
|
const session = await page.evaluate(
|
|
25
26
|
() => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session
|
|
26
27
|
)
|
|
27
28
|
await AuthenticatedUser.create({ id: session, user, session })
|
|
28
29
|
|
|
29
30
|
await page.reload({ waitUntil: 'networkidle' })
|
|
31
|
+
await waitForHydration(page)
|
|
30
32
|
const clientUser = await page.evaluate(
|
|
31
33
|
() => (window as unknown as { api: { client: { value: { user: string } } } }).api.client.value.user
|
|
32
34
|
)
|
|
33
35
|
assert.strictEqual(user, clientUser, 'client logged in')
|
|
34
36
|
|
|
35
37
|
await page.goto(env.url + '/user/settings/delete', { waitUntil: 'networkidle' })
|
|
36
|
-
await page
|
|
38
|
+
await waitForHydration(page)
|
|
39
|
+
await page.getByRole('checkbox').check()
|
|
37
40
|
await page.click('button#delete')
|
|
38
41
|
await page.waitForURL('**/delete-finished', { timeout: 10000 })
|
|
39
42
|
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import test from '
|
|
1
|
+
import { e2eSuite, test } from './e2eSuite.js'
|
|
2
|
+
import { waitForHydration } from '@live-change/e2e-test'
|
|
2
3
|
import assert from 'node:assert'
|
|
3
4
|
import App from '@live-change/framework'
|
|
4
5
|
import randomProfile from 'random-profile-generator'
|
|
5
6
|
import { withBrowser } from './withBrowser.js'
|
|
6
|
-
import { e2eSuite } from './e2eSuite.js'
|
|
7
7
|
|
|
8
8
|
const app = App.app()
|
|
9
9
|
const name = randomProfile.profile().firstName.toLowerCase()
|
|
10
10
|
const email = name + '@test.com'
|
|
11
11
|
const email2 = name + '2@test.com'
|
|
12
|
-
const happyPath = false
|
|
13
12
|
|
|
14
13
|
e2eSuite('disconnectEmail', () => {
|
|
15
14
|
test('disconnect email', async () => {
|
|
@@ -24,23 +23,23 @@ e2eSuite('disconnectEmail', () => {
|
|
|
24
23
|
await Email.create({ id: email, email, user })
|
|
25
24
|
await Email.create({ id: email2, email: email2, user })
|
|
26
25
|
await page.goto(env.url + '/', { waitUntil: 'networkidle' })
|
|
26
|
+
await waitForHydration(page)
|
|
27
27
|
const session = await page.evaluate(
|
|
28
28
|
() => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session
|
|
29
29
|
)
|
|
30
30
|
await AuthenticatedUser.create({ id: session, user, session })
|
|
31
31
|
|
|
32
32
|
await page.reload({ waitUntil: 'networkidle' })
|
|
33
|
+
await waitForHydration(page)
|
|
33
34
|
await page.goto(env.url + '/user/settings/connected', { waitUntil: 'networkidle' })
|
|
34
|
-
await page
|
|
35
|
-
await page.getByText(
|
|
35
|
+
await waitForHydration(page)
|
|
36
|
+
await page.getByText(email, { exact: true }).waitFor({ state: 'visible' })
|
|
37
|
+
await page.getByText(email2, { exact: true }).waitFor({ state: 'visible' })
|
|
36
38
|
|
|
37
|
-
await page.
|
|
38
|
-
await page.
|
|
39
|
-
assert.strictEqual(await page.getByText(
|
|
40
|
-
|
|
41
|
-
if (!happyPath) {
|
|
42
|
-
await page.locator('span.pi-times').waitFor({ state: 'hidden' })
|
|
43
|
-
}
|
|
39
|
+
await page.locator('li').filter({ hasText: email }).locator('button.p-button-rounded').click()
|
|
40
|
+
await page.locator('[role="alertdialog"] button.p-button-danger').last().click({ force: true })
|
|
41
|
+
assert.strictEqual(await page.getByText(email, { exact: true }).isVisible(), false, 'disconnected email should be gone')
|
|
42
|
+
assert.strictEqual(await page.getByText(email2, { exact: true }).isVisible(), true, 'other email should remain')
|
|
44
43
|
})
|
|
45
44
|
})
|
|
46
45
|
})
|
package/e2e/e2eSuite.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
})
|
|
10
|
-
define()
|
|
11
|
-
})
|
|
12
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
e2eSuite,
|
|
3
|
+
getE2ERegistry,
|
|
4
|
+
resetE2ERegistry,
|
|
5
|
+
setCurrentE2EFile,
|
|
6
|
+
test,
|
|
7
|
+
type E2ETestDefinition
|
|
8
|
+
} from '@live-change/e2e-test'
|
package/e2e/env.ts
CHANGED
|
@@ -1,25 +1,13 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import { fileURLToPath } from 'url'
|
|
3
|
+
import App from '@live-change/framework'
|
|
3
4
|
import { TestServer } from '@live-change/server'
|
|
5
|
+
import { createTestEnvHelpers, waitForServerReady } from '@live-change/e2e-test'
|
|
4
6
|
import appConfig from '../server/app.config.js'
|
|
5
7
|
import * as services from '../server/services.list.js'
|
|
6
8
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
async function waitForServerReady(url: string): Promise<void> {
|
|
11
|
-
const deadline = Date.now() + READY_TIMEOUT_MS
|
|
12
|
-
while (Date.now() < deadline) {
|
|
13
|
-
try {
|
|
14
|
-
const res = await fetch(url)
|
|
15
|
-
if (res.ok) return
|
|
16
|
-
} catch {
|
|
17
|
-
// not ready yet
|
|
18
|
-
}
|
|
19
|
-
await new Promise((r) => setTimeout(r, READY_POLL_MS))
|
|
20
|
-
}
|
|
21
|
-
throw new Error(`Server at ${url} did not become ready within ${READY_TIMEOUT_MS}ms`)
|
|
22
|
-
}
|
|
9
|
+
const appRuntime = App.app()
|
|
10
|
+
const internalE2EClient = { internal: true, roles: ['admin'] as const }
|
|
23
11
|
|
|
24
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
25
13
|
const serverDir = path.join(__dirname, '..', 'server')
|
|
@@ -29,16 +17,34 @@ for (const serviceConfig of appConfig.services) {
|
|
|
29
17
|
const name = (serviceConfig as { name: string }).name
|
|
30
18
|
;(serviceConfig as { module?: unknown }).module = (services as Record<string, unknown>)[name]
|
|
31
19
|
}
|
|
32
|
-
;(appConfig as { init?: (s: unknown) => Promise<void> }).init =
|
|
20
|
+
;(appConfig as { init?: (s: unknown) => Promise<void> }).init =
|
|
21
|
+
(services as { init: (s: unknown) => Promise<void> }).init
|
|
33
22
|
|
|
34
23
|
export type TestEnv = {
|
|
35
24
|
server: InstanceType<typeof TestServer>
|
|
36
25
|
url: string
|
|
37
|
-
haveService: (name: string) => {
|
|
38
|
-
|
|
26
|
+
haveService: (name: string) => {
|
|
27
|
+
name: string
|
|
28
|
+
models: Record<string, { get: (id: string) => Promise<unknown> }>
|
|
29
|
+
views: Record<string, unknown>
|
|
30
|
+
actions: Record<string, unknown>
|
|
31
|
+
triggers: Record<string, unknown>
|
|
32
|
+
}
|
|
33
|
+
haveModel: (
|
|
34
|
+
serviceName: string,
|
|
35
|
+
modelName: string
|
|
36
|
+
) => {
|
|
37
|
+
get: (id: string) => Promise<unknown>
|
|
38
|
+
create: (data: unknown) => Promise<unknown>
|
|
39
|
+
update: (id: string, data: unknown) => Promise<unknown>
|
|
40
|
+
delete: (id: string) => Promise<unknown>
|
|
41
|
+
indexObjectGet: (index: string, key: unknown, opts?: unknown) => Promise<unknown>
|
|
42
|
+
indexRangeGet: (index: string, key: unknown) => Promise<unknown[]>
|
|
43
|
+
definition: { properties: Record<string, { preFilter: (v: unknown) => unknown }> }
|
|
44
|
+
}
|
|
39
45
|
haveView: (serviceName: string, viewName: string) => unknown
|
|
40
|
-
haveAction: (serviceName: string, actionName: string) => unknown
|
|
41
|
-
haveTrigger: (serviceName: string, triggerName: string) => unknown
|
|
46
|
+
haveAction: (serviceName: string, actionName: string) => (data: unknown) => Promise<unknown>
|
|
47
|
+
haveTrigger: (serviceName: string, triggerName: string) => (data: unknown) => Promise<unknown>
|
|
42
48
|
grabObject: (serviceName: string, modelName: string, id: string) => Promise<unknown>
|
|
43
49
|
}
|
|
44
50
|
|
|
@@ -48,23 +54,11 @@ let envPromise: Promise<TestEnv> | null = null
|
|
|
48
54
|
let testServer: TestServerInstance | null = null
|
|
49
55
|
|
|
50
56
|
export async function disposeTestEnv(): Promise<void> {
|
|
51
|
-
const
|
|
57
|
+
const s = testServer
|
|
58
|
+
if (!s) return
|
|
52
59
|
testServer = null
|
|
53
60
|
envPromise = null
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function haveService(server: InstanceType<typeof TestServer>, name: string) {
|
|
58
|
-
const service = server.apiServer.services.services.find((s: { name: string }) => s.name === name)
|
|
59
|
-
if (!service) throw new Error('service ' + name + ' not found')
|
|
60
|
-
return service
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function haveModel(server: InstanceType<typeof TestServer>, serviceName: string, modelName: string) {
|
|
64
|
-
const service = haveService(server, serviceName)
|
|
65
|
-
const model = service.models[modelName]
|
|
66
|
-
if (!model) throw new Error('model ' + modelName + ' not found')
|
|
67
|
-
return model
|
|
61
|
+
await s.dispose()
|
|
68
62
|
}
|
|
69
63
|
|
|
70
64
|
export async function getTestEnv(): Promise<TestEnv> {
|
|
@@ -96,33 +90,37 @@ export async function getTestEnv(): Promise<TestEnv> {
|
|
|
96
90
|
})
|
|
97
91
|
|
|
98
92
|
const url = server.url!
|
|
93
|
+
const helpers = createTestEnvHelpers(server)
|
|
99
94
|
return {
|
|
100
95
|
server,
|
|
101
96
|
url,
|
|
102
|
-
haveService:
|
|
103
|
-
haveModel:
|
|
104
|
-
haveView:
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
97
|
+
haveService: helpers.haveService,
|
|
98
|
+
haveModel: helpers.haveModel,
|
|
99
|
+
haveView: helpers.haveView,
|
|
100
|
+
haveAction(serviceName: string, actionName: string) {
|
|
101
|
+
const action = helpers.haveAction(serviceName, actionName) as {
|
|
102
|
+
callCommand: (parameters: unknown, clientData: unknown) => Promise<unknown>
|
|
103
|
+
}
|
|
104
|
+
return (parameters: unknown) => {
|
|
105
|
+
const p = parameters as Record<string, unknown> & { _e2eGrantUser?: string }
|
|
106
|
+
const { _e2eGrantUser, ...commandParams } = p
|
|
107
|
+
const client = _e2eGrantUser
|
|
108
|
+
? { ...internalE2EClient, user: _e2eGrantUser as string }
|
|
109
|
+
: internalE2EClient
|
|
110
|
+
return action.callCommand(commandParams, client).then((res: unknown) => {
|
|
111
|
+
if (typeof res === 'string') return { id: res }
|
|
112
|
+
return res
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
115
|
},
|
|
116
|
-
haveTrigger
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
haveTrigger(serviceName: string, triggerName: string) {
|
|
117
|
+
return (data: unknown) =>
|
|
118
|
+
appRuntime.triggerService(
|
|
119
|
+
{ service: serviceName, type: triggerName, client: internalE2EClient },
|
|
120
|
+
data as Record<string, unknown>
|
|
121
|
+
)
|
|
121
122
|
},
|
|
122
|
-
grabObject:
|
|
123
|
-
const model = haveModel(server, serviceName, modelName)
|
|
124
|
-
return await model.get(id)
|
|
125
|
-
}
|
|
123
|
+
grabObject: helpers.grabObject
|
|
126
124
|
}
|
|
127
125
|
})()
|
|
128
126
|
return envPromise
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import test from '
|
|
1
|
+
import { e2eSuite, test } from './e2eSuite.js'
|
|
2
|
+
import { waitForHydration } from '@live-change/e2e-test'
|
|
2
3
|
import assert from 'node:assert'
|
|
3
4
|
import App from '@live-change/framework'
|
|
4
5
|
import randomProfile from 'random-profile-generator'
|
|
5
6
|
import passwordGenerator from 'generate-password'
|
|
6
7
|
import { withBrowser } from './withBrowser.js'
|
|
7
|
-
import { useSecretCode } from './steps.js'
|
|
8
|
-
import { e2eSuite } from './e2eSuite.js'
|
|
8
|
+
import { setPrimePasswordFieldValue, useSecretCode } from './steps.js'
|
|
9
9
|
|
|
10
10
|
const app = App.app()
|
|
11
11
|
const email = randomProfile.profile().firstName.toLowerCase() + '@test.com'
|
|
@@ -21,8 +21,10 @@ e2eSuite('resetPasswordWithEmailCode', () => {
|
|
|
21
21
|
await User.create({ id: user, roles: [] })
|
|
22
22
|
await Email.create({ id: email, email, user })
|
|
23
23
|
await page.goto(env.url + '/', { waitUntil: 'networkidle' })
|
|
24
|
+
await waitForHydration(page)
|
|
24
25
|
|
|
25
26
|
await page.goto(env.url + '/user/reset-password', { waitUntil: 'networkidle' })
|
|
27
|
+
await waitForHydration(page)
|
|
26
28
|
await page.fill('input#email', email)
|
|
27
29
|
await page.click('button[type=submit]')
|
|
28
30
|
await page.waitForURL('**/sent/*', { timeout: 10000 })
|
|
@@ -49,12 +51,12 @@ e2eSuite('resetPasswordWithEmailCode', () => {
|
|
|
49
51
|
const resetPasswordAuthenticationData = await ResetPasswordAuthentication.indexObjectGet('byKey', resetPasswordAuthentication)
|
|
50
52
|
assert.ok(resetPasswordAuthenticationData, 'reset password authentication created')
|
|
51
53
|
|
|
52
|
-
await page.getByText('Reset password').waitFor({ state: 'visible' })
|
|
54
|
+
await page.getByText('Reset password', { exact: false }).first().waitFor({ state: 'visible' })
|
|
53
55
|
|
|
54
56
|
const password =
|
|
55
57
|
passwordGenerator.generate({ length: 10, numbers: true }) + (Math.random() * 10).toFixed()
|
|
56
|
-
await page
|
|
57
|
-
await page
|
|
58
|
+
await setPrimePasswordFieldValue(page, '#newPassword', password)
|
|
59
|
+
await setPrimePasswordFieldValue(page, '#reenterPassword', password)
|
|
58
60
|
await page.click('button[type=submit]')
|
|
59
61
|
await page.waitForURL('**/reset-password-finished', { timeout: 10000 })
|
|
60
62
|
assert.ok(page.url().includes('/reset-password-finished'))
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import test from '
|
|
1
|
+
import { e2eSuite, test } from './e2eSuite.js'
|
|
2
|
+
import { waitForHydration } from '@live-change/e2e-test'
|
|
2
3
|
import assert from 'node:assert'
|
|
3
4
|
import App from '@live-change/framework'
|
|
4
5
|
import randomProfile from 'random-profile-generator'
|
|
5
6
|
import passwordGenerator from 'generate-password'
|
|
6
7
|
import { withBrowser } from './withBrowser.js'
|
|
7
|
-
import { useSecretLink } from './steps.js'
|
|
8
|
-
import { e2eSuite } from './e2eSuite.js'
|
|
8
|
+
import { setPrimePasswordFieldValue, useSecretLink } from './steps.js'
|
|
9
9
|
|
|
10
10
|
const app = App.app()
|
|
11
11
|
const email = randomProfile.profile().firstName.toLowerCase() + '@test.com'
|
|
@@ -21,8 +21,10 @@ e2eSuite('resetPasswordWithEmailLink', () => {
|
|
|
21
21
|
await User.create({ id: user, roles: [] })
|
|
22
22
|
await Email.create({ id: email, email, user })
|
|
23
23
|
await page.goto(env.url + '/', { waitUntil: 'networkidle' })
|
|
24
|
+
await waitForHydration(page)
|
|
24
25
|
|
|
25
26
|
await page.goto(env.url + '/user/reset-password', { waitUntil: 'networkidle' })
|
|
27
|
+
await waitForHydration(page)
|
|
26
28
|
await page.fill('input#email', email)
|
|
27
29
|
await page.click('button[type=submit]')
|
|
28
30
|
await page.waitForURL('**/sent/*', { timeout: 10000 })
|
|
@@ -49,12 +51,12 @@ e2eSuite('resetPasswordWithEmailLink', () => {
|
|
|
49
51
|
const resetPasswordAuthenticationData = await ResetPasswordAuthentication.indexObjectGet('byKey', resetPasswordAuthentication)
|
|
50
52
|
assert.ok(resetPasswordAuthenticationData, 'reset password authentication created')
|
|
51
53
|
|
|
52
|
-
await page.getByText('Reset password').waitFor({ state: 'visible' })
|
|
54
|
+
await page.getByText('Reset password', { exact: false }).first().waitFor({ state: 'visible' })
|
|
53
55
|
|
|
54
56
|
const password =
|
|
55
57
|
passwordGenerator.generate({ length: 10, numbers: true }) + (Math.random() * 10).toFixed()
|
|
56
|
-
await page
|
|
57
|
-
await page
|
|
58
|
+
await setPrimePasswordFieldValue(page, '#newPassword', password)
|
|
59
|
+
await setPrimePasswordFieldValue(page, '#reenterPassword', password)
|
|
58
60
|
await page.click('button[type=submit]')
|
|
59
61
|
await page.waitForURL('**/reset-password-finished', { timeout: 10000 })
|
|
60
62
|
assert.ok(page.url().includes('/reset-password-finished'))
|
package/e2e/runner.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createRunner } from '@live-change/e2e-test'
|
|
2
|
+
import { disposeTestEnv, getTestEnv } from './env.js'
|
|
3
|
+
|
|
4
|
+
const runner = createRunner({
|
|
5
|
+
setupEnv: getTestEnv,
|
|
6
|
+
teardownEnv: disposeTestEnv
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
export const runE2E = runner.runE2E
|
|
10
|
+
await runner.runCli(import.meta.url, process.argv.slice(2))
|
package/e2e/setPassword.test.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import test from '
|
|
1
|
+
import { e2eSuite, test } from './e2eSuite.js'
|
|
2
|
+
import { waitForHydration } from '@live-change/e2e-test'
|
|
2
3
|
import assert from 'node:assert'
|
|
3
4
|
import App from '@live-change/framework'
|
|
4
5
|
import randomProfile from 'random-profile-generator'
|
|
5
6
|
import passwordGenerator from 'generate-password'
|
|
7
|
+
import { setPrimePasswordFieldValue } from './steps.js'
|
|
6
8
|
import { withBrowser } from './withBrowser.js'
|
|
7
|
-
import { e2eSuite } from './e2eSuite.js'
|
|
8
9
|
|
|
9
10
|
const app = App.app()
|
|
10
11
|
const name = randomProfile.profile().firstName.toLowerCase()
|
|
@@ -22,12 +23,14 @@ e2eSuite('setPassword', () => {
|
|
|
22
23
|
await User.create({ id: user, roles: [] })
|
|
23
24
|
await Email.create({ id: email, email, user })
|
|
24
25
|
await page.goto(env.url + '/', { waitUntil: 'networkidle' })
|
|
26
|
+
await waitForHydration(page)
|
|
25
27
|
const session = await page.evaluate(
|
|
26
28
|
() => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session
|
|
27
29
|
)
|
|
28
30
|
await AuthenticatedUser.create({ id: session, user, session })
|
|
29
31
|
|
|
30
32
|
await page.reload({ waitUntil: 'networkidle' })
|
|
33
|
+
await waitForHydration(page)
|
|
31
34
|
const clientUser = await page.evaluate(
|
|
32
35
|
() => (window as unknown as { api: { client: { value: { user: string } } } }).api.client.value.user
|
|
33
36
|
)
|
|
@@ -37,28 +40,32 @@ e2eSuite('setPassword', () => {
|
|
|
37
40
|
assert.strictEqual(emptyPasswordAuthenticationData, null, 'password not set')
|
|
38
41
|
|
|
39
42
|
await page.goto(env.url + '/user/settings/change-password', { waitUntil: 'networkidle' })
|
|
40
|
-
await page
|
|
43
|
+
await waitForHydration(page)
|
|
44
|
+
await page.waitForSelector('#newPassword input', { state: 'visible', timeout: 20000 })
|
|
41
45
|
|
|
42
46
|
const firstPassword =
|
|
43
47
|
passwordGenerator.generate({ length: 10, numbers: true }) + (Math.random() * 10).toFixed()
|
|
44
|
-
await page
|
|
45
|
-
await page
|
|
46
|
-
await
|
|
47
|
-
|
|
48
|
+
await setPrimePasswordFieldValue(page, '#newPassword', firstPassword)
|
|
49
|
+
await setPrimePasswordFieldValue(page, '#reenterPassword', firstPassword)
|
|
50
|
+
await Promise.all([
|
|
51
|
+
page.waitForURL((u) => u.pathname.includes('/user/settings/change-password-finished'), { timeout: 20000 }),
|
|
52
|
+
page.locator('button[type="submit"]').click()
|
|
53
|
+
])
|
|
48
54
|
|
|
49
55
|
await new Promise((r) => setTimeout(r, 200))
|
|
50
56
|
const firstPasswordAuthenticationData = await PasswordAuthentication.get(user)
|
|
51
57
|
assert.ok(firstPasswordAuthenticationData, 'password set')
|
|
52
58
|
|
|
53
59
|
await page.goto(env.url + '/user/settings/change-password', { waitUntil: 'networkidle' })
|
|
54
|
-
await page
|
|
60
|
+
await waitForHydration(page)
|
|
61
|
+
await page.waitForSelector('#currentPassword input', { state: 'visible', timeout: 20000 })
|
|
55
62
|
|
|
56
63
|
const secondPassword =
|
|
57
64
|
passwordGenerator.generate({ length: 10, numbers: true }) + (Math.random() * 10).toFixed()
|
|
58
|
-
await page
|
|
59
|
-
await page
|
|
60
|
-
await page
|
|
61
|
-
await page.
|
|
65
|
+
await setPrimePasswordFieldValue(page, '#currentPassword', firstPassword)
|
|
66
|
+
await setPrimePasswordFieldValue(page, '#newPassword', secondPassword)
|
|
67
|
+
await setPrimePasswordFieldValue(page, '#reenterPassword', secondPassword)
|
|
68
|
+
await page.locator('button[type="submit"]').click()
|
|
62
69
|
|
|
63
70
|
await new Promise((r) => setTimeout(r, 200))
|
|
64
71
|
const secondPasswordAuthenticationData = await PasswordAuthentication.get(user)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import test from '
|
|
1
|
+
import { e2eSuite, test } from './e2eSuite.js'
|
|
2
|
+
import { waitForHydration } from '@live-change/e2e-test'
|
|
2
3
|
import assert from 'node:assert'
|
|
3
4
|
import App from '@live-change/framework'
|
|
4
5
|
import randomProfile from 'random-profile-generator'
|
|
5
6
|
import { withBrowser } from './withBrowser.js'
|
|
6
7
|
import { useSecretCode } from './steps.js'
|
|
7
|
-
import { e2eSuite } from './e2eSuite.js'
|
|
8
8
|
|
|
9
9
|
const app = App.app()
|
|
10
10
|
const randomUserData = randomProfile.profile()
|
|
@@ -25,6 +25,7 @@ e2eSuite('signInEmailCode', () => {
|
|
|
25
25
|
await Email.create({ id: email, email, user })
|
|
26
26
|
|
|
27
27
|
await page.goto(env.url + '/user/sign-in-email', { waitUntil: 'networkidle' })
|
|
28
|
+
await waitForHydration(page)
|
|
28
29
|
await page.fill('input#email', email)
|
|
29
30
|
await page.click('button[type=submit]')
|
|
30
31
|
await page.waitForURL('**/sent/*', { timeout: 10000 })
|
|
@@ -54,6 +55,7 @@ e2eSuite('signInEmailCode', () => {
|
|
|
54
55
|
|
|
55
56
|
if (!happyPath) {
|
|
56
57
|
await page.goto(url, { waitUntil: 'networkidle' })
|
|
58
|
+
await waitForHydration(page)
|
|
57
59
|
assert.ok(page.url().includes('/user/sign-in-finished'))
|
|
58
60
|
}
|
|
59
61
|
})
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import test from '
|
|
1
|
+
import { e2eSuite, test } from './e2eSuite.js'
|
|
2
|
+
import { waitForHydration } from '@live-change/e2e-test'
|
|
2
3
|
import assert from 'node:assert'
|
|
3
4
|
import App from '@live-change/framework'
|
|
4
5
|
import randomProfile from 'random-profile-generator'
|
|
5
6
|
import { withBrowser } from './withBrowser.js'
|
|
6
7
|
import { useSecretLink } from './steps.js'
|
|
7
|
-
import { e2eSuite } from './e2eSuite.js'
|
|
8
8
|
|
|
9
9
|
const app = App.app()
|
|
10
10
|
const randomUserData = randomProfile.profile()
|
|
@@ -25,6 +25,7 @@ e2eSuite('signInEmailLink', () => {
|
|
|
25
25
|
await Email.create({ id: email, email, user })
|
|
26
26
|
|
|
27
27
|
await page.goto(env.url + '/user/sign-in-email', { waitUntil: 'networkidle' })
|
|
28
|
+
await waitForHydration(page)
|
|
28
29
|
await page.fill('input#email', email)
|
|
29
30
|
await page.click('button[type=submit]')
|
|
30
31
|
await page.waitForURL('**/sent/*', { timeout: 10000 })
|
|
@@ -38,7 +39,7 @@ e2eSuite('signInEmailLink', () => {
|
|
|
38
39
|
assert.strictEqual((authenticationData as { messageData?: { user: string } })?.messageData?.user, user, 'authentication contains user')
|
|
39
40
|
|
|
40
41
|
const linkData = await useSecretLink(page, env, authentication, happyPath)
|
|
41
|
-
await page.waitForURL('**/sign-in-finished', { timeout:
|
|
42
|
+
await page.waitForURL('**/sign-in-finished', { timeout: 30000 })
|
|
42
43
|
|
|
43
44
|
assert.ok(page.url().includes('/user/sign-in-finished'))
|
|
44
45
|
const clientSession = await page.evaluate(
|
|
@@ -54,6 +55,7 @@ e2eSuite('signInEmailLink', () => {
|
|
|
54
55
|
|
|
55
56
|
if (!happyPath) {
|
|
56
57
|
await page.goto(env.url + '/user/link/' + linkData.secretCode, { waitUntil: 'networkidle' })
|
|
58
|
+
await waitForHydration(page)
|
|
57
59
|
await page.getByText('Link used').waitFor({ state: 'visible' })
|
|
58
60
|
}
|
|
59
61
|
})
|