@live-change/user-frontend 0.9.195 → 0.9.197

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.
@@ -0,0 +1,51 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert'
3
+ import App from '@live-change/framework'
4
+ import randomProfile from 'random-profile-generator'
5
+ import crypto from 'crypto'
6
+ import passwordGenerator from 'generate-password'
7
+ import { withBrowser } from './withBrowser.js'
8
+
9
+ const app = App.app()
10
+ const randomUserData = randomProfile.profile()
11
+ ;(randomUserData as { email?: string }).email =
12
+ (randomUserData as { firstName: string }).firstName.toLowerCase() + '@test.com'
13
+
14
+ test('sign in with email and password', async () => {
15
+ await withBrowser(async (page, env) => {
16
+ const user = app.generateUid()
17
+ const email = (randomUserData as { email: string }).email
18
+ const password =
19
+ passwordGenerator.generate({ length: 10, numbers: true }) + (Math.random() * 10).toFixed()
20
+ const passwordHash = crypto.createHash('sha256').update(password).digest('hex')
21
+
22
+ const User = env.haveModel('user', 'User')
23
+ const Email = env.haveModel('email', 'Email')
24
+ const PasswordAuthentication = env.haveModel('passwordAuthentication', 'PasswordAuthentication')
25
+
26
+ await User.create({ id: user, roles: [] })
27
+ await Email.create({ id: email, email, user })
28
+ await PasswordAuthentication.create({ id: user, user, passwordHash })
29
+
30
+ await page.goto(env.url + '/user/sign-in-email', { waitUntil: 'networkidle' })
31
+ await page.fill('input#email', email)
32
+ await page.fill('input[type="password"]', password)
33
+ await page.click('button[type=submit]')
34
+ await page.waitForURL('**/sign-in-finished', { timeout: 10000 })
35
+
36
+ assert.ok(page.url().includes('/user/sign-in-finished'))
37
+ await new Promise((r) => setTimeout(r, 200))
38
+
39
+ const clientSession = await page.evaluate(
40
+ () => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session
41
+ )
42
+ const AuthenticatedUser = env.haveModel('user', 'AuthenticatedUser')
43
+ const authenticatedUserData = await AuthenticatedUser.get(clientSession)
44
+ assert.ok(authenticatedUserData, 'user authenticated server-side')
45
+
46
+ const clientUser = await page.evaluate(
47
+ () => (window as unknown as { api: { client: { value: { user: string } } } }).api.client.value.user
48
+ )
49
+ assert.strictEqual(clientUser, (authenticatedUserData as { user: string }).user, 'user authenticated')
50
+ })
51
+ })
@@ -0,0 +1,45 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert'
3
+ import App from '@live-change/framework'
4
+ import randomProfile from 'random-profile-generator'
5
+ import { withBrowser } from './withBrowser.js'
6
+
7
+ const app = App.app()
8
+ const name = randomProfile.profile().firstName.toLowerCase()
9
+ const email = name + '@test.com'
10
+
11
+ test('sign out', async () => {
12
+ await withBrowser(async (page, env) => {
13
+ const user = app.generateUid()
14
+ const User = env.haveModel('user', 'User')
15
+ const Email = env.haveModel('email', 'Email')
16
+ const AuthenticatedUser = env.haveModel('user', 'AuthenticatedUser')
17
+
18
+ await User.create({ id: user, roles: [] })
19
+ await Email.create({ id: email, email, user })
20
+ await page.goto(env.url + '/', { waitUntil: 'networkidle' })
21
+ const session = await page.evaluate(
22
+ () => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session
23
+ )
24
+ await AuthenticatedUser.create({ id: session, user, session })
25
+
26
+ await page.reload({ waitUntil: 'networkidle' })
27
+ const clientUser = await page.evaluate(
28
+ () => (window as unknown as { api: { client: { value: { user: string } } } }).api.client.value.user
29
+ )
30
+ assert.strictEqual(user, clientUser, 'client logged in')
31
+
32
+ await page.goto(env.url + '/user/sign-out', { waitUntil: 'networkidle' })
33
+ await page.waitForURL('**/sign-out-finished', { timeout: 10000 })
34
+ assert.ok(page.url().includes('/user/sign-out-finished'))
35
+
36
+ await new Promise((r) => setTimeout(r, 1000))
37
+ const clientUser2 = await page.evaluate(
38
+ () => (window as unknown as { api: { client: { value: { user: unknown } } } }).api.client.value.user
39
+ )
40
+ const authenticatedUserData = await AuthenticatedUser.get(session)
41
+
42
+ assert.strictEqual(!!authenticatedUserData, false, 'no server user')
43
+ assert.strictEqual(!!clientUser2, false, 'no client user')
44
+ })
45
+ })
@@ -0,0 +1,46 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert'
3
+ import randomProfile from 'random-profile-generator'
4
+ import { withBrowser } from './withBrowser.js'
5
+ import { useSecretCode } from './steps.js'
6
+
7
+ const user = randomProfile.profile()
8
+ ;(user as { email?: string }).email =
9
+ (user as { firstName: string }).firstName.toLowerCase() + '@test.com'
10
+ const happyPath = false
11
+
12
+ test('sign up with email code', async () => {
13
+ await withBrowser(async (page, env) => {
14
+ await page.goto(env.url + '/user/sign-up-email', { waitUntil: 'networkidle' })
15
+ await page.fill('input#email', (user as { email: string }).email)
16
+ await page.click('button[type=submit]')
17
+ await page.waitForURL('**/sent/*', { timeout: 10000 })
18
+
19
+ assert.ok(page.url().includes('/sent/'))
20
+ const url = page.url()
21
+ const authentication = url.split('/').pop()!
22
+
23
+ const authenticationData = await env.grabObject('messageAuthentication', 'Authentication', authentication)
24
+ assert.ok(authenticationData, 'authentication created')
25
+
26
+ await useSecretCode(page, env, authentication, happyPath)
27
+ await page.waitForURL('**/sign-up-finished', { timeout: 10000 })
28
+
29
+ assert.ok(page.url().includes('/user/sign-up-finished'))
30
+ const clientSession = await page.evaluate(
31
+ () => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session
32
+ )
33
+ const AuthenticatedUser = env.haveModel('user', 'AuthenticatedUser')
34
+ const authenticatedUserData = await AuthenticatedUser.get(clientSession)
35
+ assert.ok(authenticatedUserData, 'user authenticated server-side')
36
+ const clientUser = await page.evaluate(
37
+ () => (window as unknown as { api: { client: { value: { user: string } } } }).api.client.value.user
38
+ )
39
+ assert.strictEqual(clientUser, (authenticatedUserData as { user: string }).user, 'user authenticated')
40
+
41
+ if (!happyPath) {
42
+ await page.goto(url, { waitUntil: 'networkidle' })
43
+ assert.ok(page.url().includes('/user/sign-up-finished'))
44
+ }
45
+ })
46
+ })
@@ -0,0 +1,46 @@
1
+ import test from 'node:test'
2
+ import assert from 'node:assert'
3
+ import randomProfile from 'random-profile-generator'
4
+ import { withBrowser } from './withBrowser.js'
5
+ import { useSecretLink } from './steps.js'
6
+
7
+ const user = randomProfile.profile()
8
+ ;(user as { email?: string }).email =
9
+ (user as { firstName: string }).firstName.toLowerCase() + '@test.com'
10
+ const happyPath = false
11
+
12
+ test('sign up with email link', async () => {
13
+ await withBrowser(async (page, env) => {
14
+ await page.goto(env.url + '/user/sign-up-email', { waitUntil: 'networkidle' })
15
+ await page.fill('input#email', (user as { email: string }).email)
16
+ await page.click('button[type=submit]')
17
+ await page.waitForURL('**/sent/*', { timeout: 10000 })
18
+
19
+ assert.ok(page.url().includes('/sent/'))
20
+ const url = page.url()
21
+ const authentication = url.split('/').pop()!
22
+
23
+ const authenticationData = await env.grabObject('messageAuthentication', 'Authentication', authentication)
24
+ assert.ok(authenticationData, 'authentication created')
25
+
26
+ const linkData = await useSecretLink(page, env, authentication, happyPath)
27
+ await page.waitForURL('**/sign-up-finished', { timeout: 10000 })
28
+
29
+ assert.ok(page.url().includes('/user/sign-up-finished'))
30
+ const clientSession = await page.evaluate(
31
+ () => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session
32
+ )
33
+ const AuthenticatedUser = env.haveModel('user', 'AuthenticatedUser')
34
+ const authenticatedUserData = await AuthenticatedUser.get(clientSession)
35
+ assert.ok(authenticatedUserData, 'user authenticated server-side')
36
+ const clientUser = await page.evaluate(
37
+ () => (window as unknown as { api: { client: { value: { user: string } } } }).api.client.value.user
38
+ )
39
+ assert.strictEqual(clientUser, (authenticatedUserData as { user: string }).user, 'user authenticated')
40
+
41
+ if (!happyPath) {
42
+ await page.goto(env.url + '/user/link/' + linkData.secretCode, { waitUntil: 'networkidle' })
43
+ await page.getByText('Link used').waitFor({ state: 'visible' })
44
+ }
45
+ })
46
+ })
package/e2e/steps.ts ADDED
@@ -0,0 +1,179 @@
1
+ import assert from 'node:assert'
2
+ import type { Page } from 'playwright'
3
+ import App from '@live-change/framework'
4
+ import randomProfile from 'random-profile-generator'
5
+ import passwordGenerator from 'generate-password'
6
+ import type { TestEnv } from './env.js'
7
+
8
+ const app = App.app()
9
+
10
+ export async function haveUser(
11
+ env: TestEnv,
12
+ name?: string,
13
+ email?: string,
14
+ password?: string,
15
+ user: string = app.generateUid(),
16
+ roles: string[] = []
17
+ ): Promise<{ id: string; name: string; email: string; password: string }> {
18
+ if (!password) password = passwordGenerator.generate({ length: 10, numbers: true })
19
+ if (!name) name = randomProfile.profile().firstName
20
+ if (!email) {
21
+ name = name!
22
+ email = name.split(' ')[0].toLowerCase() + (Math.random() * 100).toFixed() + '@test.com'
23
+ }
24
+
25
+ const PasswordAuthentication = env.haveModel('passwordAuthentication', 'PasswordAuthentication')
26
+ const User = env.haveModel('user', 'User')
27
+ const Email = env.haveModel('email', 'Email')
28
+ const Identification = env.haveModel('userIdentification', 'Identification')
29
+
30
+ const passwordHash = PasswordAuthentication.definition.properties.passwordHash.preFilter(password)
31
+ await User.create({ id: user, roles })
32
+ await PasswordAuthentication.create({ id: user, user, passwordHash })
33
+ await Email.create({ id: email, email, user })
34
+ await Identification.create({
35
+ id: App.encodeIdentifier(['user_User', user]),
36
+ sessionOrUserType: 'user_User',
37
+ sessionOrUser: user,
38
+ name: name!
39
+ })
40
+ return { id: user, name: name!, email, password }
41
+ }
42
+
43
+ export async function useEmailLink(
44
+ page: Page,
45
+ env: TestEnv,
46
+ email: string,
47
+ prefix = '/user/link/'
48
+ ): Promise<{ authentication: unknown; link: { secretCode: string } }> {
49
+ const MessageAuthentication = env.haveModel('messageAuthentication', 'Authentication') as {
50
+ indexObjectGet: (index: string, key: unknown[], opts?: { reverse?: boolean; limit?: number }) => Promise<unknown>
51
+ }
52
+ const authentication = await MessageAuthentication.indexObjectGet('byContact', ['email', email], {
53
+ reverse: true,
54
+ limit: 1
55
+ })
56
+ const Link = env.haveModel('secretLink', 'Link') as {
57
+ indexObjectGet: (index: string, key: unknown) => Promise<{ secretCode: string }>
58
+ }
59
+ const link = await Link.indexObjectGet('byAuthentication', authentication)
60
+ await page.goto(env.url + prefix + link.secretCode, { waitUntil: 'networkidle' })
61
+ await new Promise((r) => setTimeout(r, 100))
62
+ return { authentication, link }
63
+ }
64
+
65
+ export async function amLoggedIn(page: Page, env: TestEnv, user: { id: string }): Promise<void> {
66
+ const AuthenticatedUser = env.haveModel('user', 'AuthenticatedUser')
67
+ const session = await page.evaluate(() => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session)
68
+ await AuthenticatedUser.create({ id: session, session, user: user.id })
69
+ }
70
+
71
+ export async function amLoggedOut(page: Page, env: TestEnv): Promise<void> {
72
+ const AuthenticatedUser = env.haveModel('user', 'AuthenticatedUser')
73
+ const session = await page.evaluate(() => (window as unknown as { api: { client: { value: { session: string } } } }).api.client.value.session)
74
+ await AuthenticatedUser.delete(session)
75
+ }
76
+
77
+ export async function useSecretCode(
78
+ page: Page,
79
+ env: TestEnv,
80
+ authentication: unknown,
81
+ happyPath: boolean
82
+ ): Promise<void> {
83
+ const Code = env.haveModel('secretCode', 'Code') as {
84
+ indexObjectGet: (index: string, key: unknown) => Promise<{ id: string; secretCode: string; expire: Date }>
85
+ indexRangeGet: (index: string, key: unknown) => Promise<{ id: string; secretCode: string; expire: Date }[]>
86
+ update: (id: string, data: { expire: Date }) => Promise<unknown>
87
+ }
88
+ let codeData = await Code.indexObjectGet('byAuthentication', authentication)
89
+ assert.ok(codeData, 'code created')
90
+
91
+ if (!happyPath) {
92
+ await sleep(200)
93
+ await page.waitForSelector('input#code', { state: 'visible' })
94
+ const wrongCode = codeData!.secretCode === '123456' ? '654321' : '123456'
95
+ await page.fill('input#code', wrongCode)
96
+ await page.fill('input#code', wrongCode)
97
+ await page.click('button[type=submit]')
98
+ // PrimeVue 4 uses <Message> with role=\"alert\" instead of a #code-help.p-error span
99
+ await page.getByRole('alert').waitFor({ state: 'visible' })
100
+ }
101
+
102
+ if (!happyPath) {
103
+ await sleep(200)
104
+ await page.waitForSelector('input#code', { state: 'visible' })
105
+ await Code.update(codeData!.id, { expire: new Date() })
106
+ await page.fill('input#code', codeData!.secretCode)
107
+ await page.fill('input#code', codeData!.secretCode)
108
+ await page.click('button[type=submit]')
109
+ await page.getByRole('alert').waitFor({ state: 'visible' })
110
+
111
+ await page.click('text=Resend')
112
+ assert.ok(page.url().includes('/sent/'))
113
+
114
+ await new Promise((r) => setTimeout(r, 200))
115
+ const newCodeData = await Code.indexRangeGet('byAuthentication', authentication)
116
+ newCodeData.sort((a, b) => new Date(b.expire).getTime() - new Date(a.expire).getTime())
117
+ const oldCodeData = codeData
118
+ codeData = newCodeData[0]
119
+ assert.ok(codeData, 'code exists')
120
+ assert.notStrictEqual(oldCodeData!.id, codeData!.id, 'code is different from previous code')
121
+ }
122
+
123
+ await page.waitForFunction(() => {
124
+ const input = document.querySelector('input#code') as HTMLInputElement
125
+ if(!input) return false
126
+ return input.value === ''
127
+ })
128
+ await page.fill('input#code', codeData!.secretCode)
129
+ await page.fill('input#code', codeData!.secretCode)
130
+ await page.click('button[type=submit]')
131
+ await new Promise((r) => setTimeout(r, 100))
132
+ }
133
+
134
+ export async function useSecretLink(
135
+ page: Page,
136
+ env: TestEnv,
137
+ authentication: unknown,
138
+ happyPath: boolean,
139
+ prefix = '/user'
140
+ ): Promise<{ secretCode: string }> {
141
+ const Link = env.haveModel('secretLink', 'Link') as {
142
+ indexObjectGet: (index: string, key: unknown) => Promise<{ id: string; secretCode: string; expire: Date }>
143
+ indexRangeGet: (index: string, key: unknown) => Promise<{ id: string; secretCode: string; expire: Date }[]>
144
+ update: (id: string, data: { expire: Date }) => Promise<unknown>
145
+ }
146
+ let linkData = await Link.indexObjectGet('byAuthentication', authentication)
147
+ assert.ok(linkData, 'link created')
148
+
149
+ if (!happyPath) {
150
+ await page.goto(env.url + prefix + '/link/[badSecret]', { waitUntil: 'networkidle' })
151
+ await page.getByText('Unknown link').waitFor({ state: 'visible' })
152
+ }
153
+
154
+ if (!happyPath) {
155
+ await new Promise((r) => setTimeout(r, 200))
156
+ await Link.update(linkData!.id, { expire: new Date() })
157
+ await page.goto(env.url + prefix + '/link/' + linkData!.secretCode, { waitUntil: 'networkidle' })
158
+ await page.getByText('Link expired').waitFor({ state: 'visible' })
159
+
160
+ await page.click('text=Resend')
161
+ assert.ok(page.url().includes(prefix + '/sent/'))
162
+
163
+ await new Promise((r) => setTimeout(r, 200))
164
+ const newLinksData = await Link.indexRangeGet('byAuthentication', authentication)
165
+ newLinksData.sort((a, b) => new Date(b.expire).getTime() - new Date(a.expire).getTime())
166
+ const oldLinkData = linkData
167
+ linkData = newLinksData[0]
168
+ assert.ok(linkData, 'link exists')
169
+ assert.notStrictEqual(oldLinkData!.id, linkData!.id, 'link is different from previous link')
170
+ }
171
+
172
+ await page.goto(env.url + prefix + '/link/' + linkData!.secretCode, { waitUntil: 'networkidle' })
173
+ await new Promise((r) => setTimeout(r, 100))
174
+ return linkData!
175
+ }
176
+
177
+ export async function sleep(ms: number) {
178
+ return new Promise((r) => setTimeout(r, ms))
179
+ }
@@ -0,0 +1,19 @@
1
+ import { chromium } from 'playwright'
2
+ import { getTestEnv } from './env.js'
3
+ import type { TestEnv } from './env.js'
4
+ import type { Page } from 'playwright'
5
+
6
+ export async function withBrowser(
7
+ fn: (page: Page, env: TestEnv) => Promise<void>
8
+ ): Promise<void> {
9
+ const env = await getTestEnv()
10
+ const browser = await chromium.launch({ headless: false })
11
+ const context = await browser.newContext()
12
+ const page = await context.newPage()
13
+ try {
14
+ await fn(page, env)
15
+ } finally {
16
+ await context.close()
17
+ await browser.close()
18
+ }
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/user-frontend",
3
- "version": "0.9.195",
3
+ "version": "0.9.197",
4
4
  "scripts": {
5
5
  "memDev": "tsx --inspect --expose-gc server/start.js memDev --enableSessions --initScript ./init.js --dbAccess",
6
6
  "localDevInit": "tsx server/start.js localDev --enableSessions --initScript ./init.js --dbAccess",
@@ -36,29 +36,29 @@
36
36
  },
37
37
  "type": "module",
38
38
  "dependencies": {
39
- "@live-change/cli": "^0.9.195",
40
- "@live-change/dao": "^0.9.195",
41
- "@live-change/dao-vue3": "^0.9.195",
42
- "@live-change/dao-websocket": "^0.9.195",
43
- "@live-change/email-service": "^0.9.195",
44
- "@live-change/framework": "^0.9.195",
45
- "@live-change/identicon-service": "^0.9.195",
46
- "@live-change/image-frontend": "^0.9.195",
47
- "@live-change/message-authentication-service": "^0.9.195",
48
- "@live-change/notification-service": "^0.9.195",
49
- "@live-change/password-authentication-service": "^0.9.195",
50
- "@live-change/pattern": "^0.9.195",
51
- "@live-change/secret-code-service": "^0.9.195",
52
- "@live-change/secret-link-service": "^0.9.195",
53
- "@live-change/security-frontend": "^0.9.195",
54
- "@live-change/security-service": "^0.9.195",
55
- "@live-change/session-service": "^0.9.195",
56
- "@live-change/timer-service": "^0.9.195",
57
- "@live-change/upload-service": "^0.9.195",
58
- "@live-change/user-identification-service": "^0.9.195",
59
- "@live-change/user-service": "^0.9.195",
60
- "@live-change/vue3-components": "^0.9.195",
61
- "@live-change/vue3-ssr": "^0.9.195",
39
+ "@live-change/cli": "^0.9.197",
40
+ "@live-change/dao": "^0.9.197",
41
+ "@live-change/dao-vue3": "^0.9.197",
42
+ "@live-change/dao-websocket": "^0.9.197",
43
+ "@live-change/email-service": "^0.9.197",
44
+ "@live-change/framework": "^0.9.197",
45
+ "@live-change/identicon-service": "^0.9.197",
46
+ "@live-change/image-frontend": "^0.9.197",
47
+ "@live-change/message-authentication-service": "^0.9.197",
48
+ "@live-change/notification-service": "^0.9.197",
49
+ "@live-change/password-authentication-service": "^0.9.197",
50
+ "@live-change/pattern": "^0.9.197",
51
+ "@live-change/secret-code-service": "^0.9.197",
52
+ "@live-change/secret-link-service": "^0.9.197",
53
+ "@live-change/security-frontend": "^0.9.197",
54
+ "@live-change/security-service": "^0.9.197",
55
+ "@live-change/session-service": "^0.9.197",
56
+ "@live-change/timer-service": "^0.9.197",
57
+ "@live-change/upload-service": "^0.9.197",
58
+ "@live-change/user-identification-service": "^0.9.197",
59
+ "@live-change/user-service": "^0.9.197",
60
+ "@live-change/vue3-components": "^0.9.197",
61
+ "@live-change/vue3-ssr": "^0.9.197",
62
62
  "@vueuse/core": "^12.3.0",
63
63
  "codeceptjs-assert": "^0.0.5",
64
64
  "codeceptjs-video-helper": "0.1.3",
@@ -79,8 +79,8 @@
79
79
  "wtfnode": "^0.9.1"
80
80
  },
81
81
  "devDependencies": {
82
- "@live-change/codeceptjs-helper": "^0.9.195",
83
- "codeceptjs": "^3.6.10",
82
+ "@live-change/codeceptjs-helper": "^0.9.197",
83
+ "codeceptjs": "^3.7.6",
84
84
  "generate-password": "1.7.1",
85
85
  "playwright": "1.49.1",
86
86
  "random-profile-generator": "^2.3.0",
@@ -90,5 +90,5 @@
90
90
  "author": "Michał Łaszczewski <michal@laszczewski.pl>",
91
91
  "license": "BSD-3-Clause",
92
92
  "description": "",
93
- "gitHead": "9f61eddae56ddd89aba20988b8e208d948728496"
93
+ "gitHead": "8231c2ed8bc3beed2c732aa5727174417f19082b"
94
94
  }
@@ -1,8 +1,35 @@
1
+ import dotenv from 'dotenv'
2
+ dotenv.config()
3
+
1
4
  import App from "@live-change/framework"
2
5
  const app = App.app()
3
6
 
4
- import dotenv from 'dotenv'
5
- dotenv.config()
7
+ import { fileURLToPath } from 'url'
8
+ import { dirname, join } from 'path'
9
+ import { accessSync, readFileSync } from 'fs'
10
+
11
+ const packageJsonPath = dirname(fileURLToPath(import.meta.url))
12
+ .split('/').map((part, i, arr) =>
13
+ join(arr.slice(0, arr.length - i).join('/'), 'package.json')
14
+ ).find(p => { try { accessSync(p); return true } catch(e) { return false }})
15
+ const packageJson = packageJsonPath ? JSON.parse(readFileSync(packageJsonPath, 'utf-8')) : {}
16
+
17
+ const name = packageJson.name ?? "Example"
18
+ const brandName = process.env.BRAND_NAME || (name[0].toUpperCase() + name.slice(1))
19
+ const homepage = process.env.BASE_HREF ?? packageJson.homepage
20
+ const brandDomain = process.env.BRAND_DOMAIN ||
21
+ (homepage && homepage.match(/https\:\/\/([^\/]+)/)?.[1]) || 'example.com'
22
+ const baseHref = process.env.BASE_HREF || homepage || 'http://localhost:8001'
23
+ const version = process.env.VERSION || packageJson.version
24
+
25
+ const clientConfig = {
26
+ version,
27
+ name, brandName, brandDomain, homepage, baseHref
28
+ }
29
+ const baseAppConfig = {
30
+ ...clientConfig,
31
+ clientConfig: { ...clientConfig },
32
+ }
6
33
 
7
34
  const contactTypes = ['email', 'phone']
8
35
  const remoteAccountTypes = ['google', 'linkedin']
@@ -10,6 +37,10 @@ const remoteAccountTypes = ['google', 'linkedin']
10
37
  import securityConfig from './security.config.js'
11
38
 
12
39
  app.config = {
40
+ ...baseAppConfig,
41
+ clientConfig: {
42
+ ...baseAppConfig.clientConfig,
43
+ },
13
44
  services: [
14
45
  {
15
46
  name: 'timer',
@@ -1,60 +0,0 @@
1
- import { devices } from 'playwright'
2
-
3
- const testServerPort = process.env.TEST_URL ? 0 : require('get-port-sync')()
4
- const testServerUrl = process.env.TEST_URL || `http://localhost:${testServerPort}`
5
-
6
- const device = devices['Pixel 2']
7
-
8
- exports.config = {
9
- tests: './*.test.js',
10
- output: './output',
11
- helpers: {
12
- LiveChange: {
13
- require: '@live-change/codeceptjs-helper',
14
- startServer: !process.env.TEST_URL,
15
- enableSessions: true,
16
- initScript: "./init.js",
17
- port: testServerPort,
18
- dev: true
19
- },
20
- VideoHelper: {
21
- require: 'codeceptjs-video-helper'
22
- },
23
- AssertWrapper : {
24
- require: "codeceptjs-assert"
25
- },
26
- Playwright: {
27
- browser: 'chromium',
28
- url: testServerUrl,
29
- show: true,
30
- emulate: {
31
- ...device,
32
- recordVideo: process.env.RECORD_TESTS ? {
33
- dir: "./output",
34
- //size: { width: 1080, height: 1920 }
35
- } : undefined,
36
- },
37
- chromium: {
38
- args: [`--force-device-scale-factor=${device.deviceScaleFactor}`]
39
- }
40
- }
41
- },
42
- include: {
43
- I: './steps_file.js'
44
- },
45
- bootstrap: null,
46
- mocha: {},
47
- name: 'e2e',
48
- plugins: {
49
- pauseOnFail: {},
50
- retryFailedStep: {
51
- enabled: true
52
- },
53
- tryTo: {
54
- enabled: true
55
- },
56
- screenshotOnFail: {
57
- enabled: true
58
- }
59
- }
60
- }
@@ -1,61 +0,0 @@
1
- const app = require('@live-change/framework').app()
2
- const randomProfile = require('random-profile-generator')
3
-
4
- const email = randomProfile.profile().firstName.toLowerCase() + '@test.com' // test domain - emails not sent
5
- const email2 = randomProfile.profile().firstName.toLowerCase() + '2@test.com' // test domain - emails not sent
6
-
7
- const happyPath = false
8
-
9
- Feature('user')
10
-
11
- Scenario('connect email with code', async ({ I }) => {
12
-
13
- const user = app.generateUid()
14
-
15
- const User = await I.haveModel('user', 'User')
16
- const Email = await I.haveModel('email', 'Email')
17
- const AuthenticatedUser = await I.haveModel('user', 'AuthenticatedUser')
18
-
19
- await User.create({ id: user, roles: [] })
20
- await Email.create({ id: email, email, user })
21
- I.amOnPage('/')
22
- const session = await I.executeScript(() => api.client.value.session)
23
- await AuthenticatedUser.create({ id: session, user, session })
24
-
25
- I.amOnPage('/connected')
26
- I.see(email)
27
- I.dontSee(email2)
28
-
29
- I.click('button#connect')
30
-
31
- I.seeInCurrentUrl('/connect')
32
-
33
- if(!happyPath) {
34
- I.fillField('input#email', email)
35
- I.click('button[type=submit]')
36
- I.seeInCurrentUrl('/connect')
37
- }
38
-
39
- I.fillField('input#email', email2)
40
- I.click('button[type=submit]')
41
-
42
- I.seeInCurrentUrl('/sent/')
43
-
44
- const url = await I.grabCurrentUrl()
45
- const authentication = url.split('/').pop()
46
-
47
- const authenticationData = await I.grabObject('messageAuthentication', 'Authentication', authentication)
48
- console.log("AUTHENTICATION DATA", authenticationData)
49
- I.assert(!!authenticationData, true, 'authentication created')
50
- I.assert(authenticationData?.messageData?.user, user, 'authentication contains user')
51
-
52
- await I.useSecretCode(authentication, happyPath)
53
-
54
- I.seeInCurrentUrl('/connect-finished')
55
-
56
- if(!happyPath) {
57
- I.amOnPage(url)
58
- I.seeInCurrentUrl('/connect-finished')
59
- }
60
-
61
- })