@atproto/pds 0.4.217 → 0.4.219

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.
Files changed (126) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/account-manager/oauth-store.d.ts.map +1 -1
  3. package/dist/account-manager/oauth-store.js +1 -1
  4. package/dist/account-manager/oauth-store.js.map +1 -1
  5. package/dist/actor-store/blob/transactor.d.ts +6 -6
  6. package/dist/actor-store/blob/transactor.d.ts.map +1 -1
  7. package/dist/actor-store/blob/transactor.js +1 -4
  8. package/dist/actor-store/blob/transactor.js.map +1 -1
  9. package/dist/api/com/atproto/repo/importRepo.js +1 -1
  10. package/dist/api/com/atproto/repo/importRepo.js.map +1 -1
  11. package/dist/api/com/atproto/repo/putRecord.js.map +1 -1
  12. package/dist/config/config.d.ts.map +1 -1
  13. package/dist/config/config.js +7 -3
  14. package/dist/config/config.js.map +1 -1
  15. package/dist/config/env.d.ts +4 -0
  16. package/dist/config/env.d.ts.map +1 -1
  17. package/dist/config/env.js +4 -0
  18. package/dist/config/env.js.map +1 -1
  19. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts +1 -0
  20. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts.map +1 -1
  21. package/dist/lexicons/app/bsky/actor/defs.defs.js +1 -0
  22. package/dist/lexicons/app/bsky/actor/defs.defs.js.map +1 -1
  23. package/dist/lexicons/app/bsky/actor/profile.defs.d.ts.map +1 -1
  24. package/dist/lexicons/app/bsky/actor/profile.defs.js +2 -10
  25. package/dist/lexicons/app/bsky/actor/profile.defs.js.map +1 -1
  26. package/dist/lexicons/app/bsky/embed/external.defs.d.ts.map +1 -1
  27. package/dist/lexicons/app/bsky/embed/external.defs.js +1 -1
  28. package/dist/lexicons/app/bsky/embed/external.defs.js.map +1 -1
  29. package/dist/lexicons/app/bsky/embed/images.defs.d.ts +3 -0
  30. package/dist/lexicons/app/bsky/embed/images.defs.d.ts.map +1 -1
  31. package/dist/lexicons/app/bsky/embed/images.defs.js +1 -5
  32. package/dist/lexicons/app/bsky/embed/images.defs.js.map +1 -1
  33. package/dist/lexicons/app/bsky/embed/video.defs.d.ts.map +1 -1
  34. package/dist/lexicons/app/bsky/embed/video.defs.js +2 -6
  35. package/dist/lexicons/app/bsky/embed/video.defs.js.map +1 -1
  36. package/dist/lexicons/app/bsky/feed/generator.defs.d.ts.map +1 -1
  37. package/dist/lexicons/app/bsky/feed/generator.defs.js +1 -5
  38. package/dist/lexicons/app/bsky/feed/generator.defs.js.map +1 -1
  39. package/dist/lexicons/app/bsky/graph/list.defs.d.ts.map +1 -1
  40. package/dist/lexicons/app/bsky/graph/list.defs.js +1 -5
  41. package/dist/lexicons/app/bsky/graph/list.defs.js.map +1 -1
  42. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscover.d.ts +3 -0
  43. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscover.d.ts.map +1 -0
  44. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscover.defs.d.ts +22 -0
  45. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscover.defs.d.ts.map +1 -0
  46. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscover.defs.js +53 -0
  47. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscover.defs.js.map +1 -0
  48. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscover.js +45 -0
  49. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscover.js.map +1 -0
  50. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscoverSkeleton.d.ts +3 -0
  51. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscoverSkeleton.d.ts.map +1 -0
  52. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscoverSkeleton.defs.d.ts +31 -0
  53. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscoverSkeleton.defs.d.ts.map +1 -0
  54. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscoverSkeleton.defs.js +20 -0
  55. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscoverSkeleton.defs.js.map +1 -0
  56. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscoverSkeleton.js +45 -0
  57. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForDiscoverSkeleton.js.map +1 -0
  58. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExplore.d.ts +3 -0
  59. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExplore.d.ts.map +1 -0
  60. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExplore.defs.d.ts +24 -0
  61. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExplore.defs.d.ts.map +1 -0
  62. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExplore.defs.js +54 -0
  63. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExplore.defs.js.map +1 -0
  64. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExplore.js +45 -0
  65. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExplore.js.map +1 -0
  66. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExploreSkeleton.d.ts +3 -0
  67. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExploreSkeleton.d.ts.map +1 -0
  68. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExploreSkeleton.defs.d.ts +33 -0
  69. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExploreSkeleton.defs.d.ts.map +1 -0
  70. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExploreSkeleton.defs.js +21 -0
  71. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExploreSkeleton.defs.js.map +1 -0
  72. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExploreSkeleton.js +45 -0
  73. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForExploreSkeleton.js.map +1 -0
  74. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMore.d.ts +3 -0
  75. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMore.d.ts.map +1 -0
  76. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMore.defs.d.ts +24 -0
  77. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMore.defs.d.ts.map +1 -0
  78. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMore.defs.js +54 -0
  79. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMore.defs.js.map +1 -0
  80. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMore.js +45 -0
  81. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMore.js.map +1 -0
  82. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMoreSkeleton.d.ts +3 -0
  83. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMoreSkeleton.d.ts.map +1 -0
  84. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMoreSkeleton.defs.d.ts +33 -0
  85. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMoreSkeleton.defs.d.ts.map +1 -0
  86. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMoreSkeleton.defs.js +21 -0
  87. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMoreSkeleton.defs.js.map +1 -0
  88. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMoreSkeleton.js +45 -0
  89. package/dist/lexicons/app/bsky/unspecced/getSuggestedUsersForSeeMoreSkeleton.js.map +1 -0
  90. package/dist/lexicons/app/bsky/unspecced.d.ts +6 -0
  91. package/dist/lexicons/app/bsky/unspecced.d.ts.map +1 -1
  92. package/dist/lexicons/app/bsky/unspecced.js +7 -1
  93. package/dist/lexicons/app/bsky/unspecced.js.map +1 -1
  94. package/dist/lexicons/app/bsky/video/defs.defs.js +1 -1
  95. package/dist/lexicons/app/bsky/video/defs.defs.js.map +1 -1
  96. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts +2 -6
  97. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts.map +1 -1
  98. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js +1 -1
  99. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js.map +1 -1
  100. package/dist/pipethrough.d.ts.map +1 -1
  101. package/dist/pipethrough.js +11 -7
  102. package/dist/pipethrough.js.map +1 -1
  103. package/dist/read-after-write/viewer.d.ts.map +1 -1
  104. package/dist/read-after-write/viewer.js +7 -6
  105. package/dist/read-after-write/viewer.js.map +1 -1
  106. package/dist/repo/prepare.d.ts.map +1 -1
  107. package/dist/repo/prepare.js.map +1 -1
  108. package/dist/repo/types.d.ts +3 -3
  109. package/dist/repo/types.d.ts.map +1 -1
  110. package/dist/repo/types.js.map +1 -1
  111. package/package.json +13 -13
  112. package/src/account-manager/oauth-store.ts +2 -1
  113. package/src/actor-store/blob/transactor.ts +9 -15
  114. package/src/api/com/atproto/repo/importRepo.ts +1 -1
  115. package/src/api/com/atproto/repo/putRecord.ts +2 -2
  116. package/src/config/config.ts +13 -3
  117. package/src/config/env.ts +4 -0
  118. package/src/pipethrough.ts +15 -5
  119. package/src/read-after-write/viewer.ts +10 -7
  120. package/src/repo/prepare.ts +2 -2
  121. package/src/repo/types.ts +3 -3
  122. package/tests/_oauth_client_assets_middleware.ts +23 -0
  123. package/tests/_puppeteer.ts +71 -17
  124. package/tests/file-uploads.test.ts +17 -9
  125. package/tests/oauth.test.ts +52 -115
  126. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,27 +1,71 @@
1
1
  import assert from 'node:assert'
2
- import { type Browser, type Page } from 'puppeteer'
2
+ import { type Browser, Handler, type Page, Target, TargetType } from 'puppeteer'
3
3
 
4
4
  export class PageHelper implements AsyncDisposable {
5
5
  constructor(protected readonly page: Page) {}
6
6
 
7
- async goto(url: string) {
8
- await this.page.goto(url)
7
+ async goto(url: string | URL) {
8
+ await this.page.goto(url.toString())
9
+ }
10
+
11
+ isClosed() {
12
+ return this.page.isClosed()
13
+ }
14
+
15
+ async title() {
16
+ await this.waitForNetworkIdle()
17
+ return this.page.title()
9
18
  }
10
19
 
11
20
  async waitForNetworkIdle() {
12
21
  await this.page.waitForNetworkIdle()
13
22
  }
14
23
 
15
- async navigationAction(run: () => Promise<unknown>): Promise<void> {
16
- const promise = this.page.waitForNavigation()
24
+ async waitForPopup(run: () => Promise<unknown>): Promise<PageHelper> {
25
+ const browser = this.page.browser()
26
+ const popupPromise = new Promise<Page | null>((resolve, reject) => {
27
+ const cleanup = () => {
28
+ clearTimeout(timeout)
29
+ browser.off('targetcreated', targetcreated)
30
+ }
31
+
32
+ const timeout = setTimeout(() => {
33
+ cleanup()
34
+ reject(new Error('Timeout waiting for popup'))
35
+ }, 5_000)
36
+
37
+ const targetcreated: Handler<Target> = async (target) => {
38
+ switch (target.type()) {
39
+ case TargetType.BACKGROUND_PAGE:
40
+ case TargetType.PAGE: {
41
+ cleanup()
42
+ resolve(target.page())
43
+ }
44
+ }
45
+ }
46
+
47
+ browser.once('targetcreated', targetcreated)
48
+ })
49
+
50
+ await run()
51
+ const popup = await popupPromise
52
+ assert(popup, 'Popup page not found')
53
+
54
+ return new PageHelper(popup)
55
+ }
56
+
57
+ async navigationAction(run: () => unknown | Promise<unknown>): Promise<void> {
58
+ const promise = this.page.waitForNavigation({ timeout: 10_000 })
17
59
  await run()
18
60
  await promise
19
- await this.waitForNetworkIdle()
20
61
  }
21
62
 
22
- async checkTitle(expected: string) {
23
- await this.waitForNetworkIdle()
24
- await expect(this.page.title()).resolves.toBe(expected)
63
+ async navigationClick(text: string, tag = 'button') {
64
+ return this.navigationAction(() => this.clickOnText(text, tag))
65
+ }
66
+
67
+ async assertTitle(expected: string) {
68
+ await expect(this.title()).resolves.toBe(expected)
25
69
  }
26
70
 
27
71
  async clickOn(selector: string) {
@@ -30,8 +74,12 @@ export class PageHelper implements AsyncDisposable {
30
74
  return elementHandle
31
75
  }
32
76
 
33
- async clickOnButton(text: string) {
34
- return this.clickOn(`button::-p-text(${JSON.stringify(text)})`)
77
+ async clickOnText(text: string, tag = 'button') {
78
+ return this.clickOn(`${tag}::-p-text(${JSON.stringify(text)})`)
79
+ }
80
+
81
+ async clickOnAriaLabel(label: string, tag = 'button') {
82
+ return this.clickOn(`${tag}[aria-label=${JSON.stringify(label)}]`)
35
83
  }
36
84
 
37
85
  async typeIn(selector: string, text: string) {
@@ -46,16 +94,22 @@ export class PageHelper implements AsyncDisposable {
46
94
  }
47
95
 
48
96
  async ensureTextVisibility(text: string, tag = 'p') {
49
- await this.page.waitForSelector(`${tag}::-p-text(${JSON.stringify(text)})`)
97
+ await this.page.waitForSelector(
98
+ `${tag}::-p-text(${JSON.stringify(text)})`,
99
+ {
100
+ visible: true,
101
+ timeout: 5_000,
102
+ },
103
+ )
50
104
  }
51
105
 
52
106
  protected async getVisibleElement(selector: string) {
53
- const elementHandle = await this.page.waitForSelector(selector)
54
-
55
- expect(elementHandle).not.toBeNull()
56
- assert(elementHandle)
107
+ const elementHandle = await this.page.waitForSelector(selector, {
108
+ visible: true,
109
+ timeout: 5_000,
110
+ })
57
111
 
58
- await expect(elementHandle.isVisible()).resolves.toBe(true)
112
+ assert(elementHandle, `Element not found: ${selector}`)
59
113
 
60
114
  return elementHandle
61
115
  }
@@ -1,10 +1,15 @@
1
+ import assert from 'node:assert'
1
2
  import fs from 'node:fs/promises'
2
3
  import { gzipSync } from 'node:zlib'
3
4
  import * as uint8arrays from 'uint8arrays'
4
5
  import { randomBytes } from '@atproto/crypto'
5
6
  import { SeedClient, TestNetworkNoAppView } from '@atproto/dev-env'
6
7
  import { Client, DidString } from '@atproto/lex'
7
- import { BlobRef } from '@atproto/lex-data'
8
+ import {
9
+ TypedBlobRef,
10
+ getBlobCidString,
11
+ isTypedBlobRef,
12
+ } from '@atproto/lex-data'
8
13
  import { AppContext } from '../src'
9
14
  import { ActorDb } from '../src/actor-store/db'
10
15
  import { DiskBlobStore } from '../src/disk-blobstore'
@@ -40,7 +45,7 @@ describe('file uploads', () => {
40
45
  await network.close()
41
46
  })
42
47
 
43
- let smallBlob: BlobRef
48
+ let smallBlob: TypedBlobRef
44
49
  let smallFile: Uint8Array
45
50
 
46
51
  it('handles client abort', async () => {
@@ -78,6 +83,7 @@ describe('file uploads', () => {
78
83
  headers: sc.getHeaders(alice),
79
84
  encoding: 'image/jpeg',
80
85
  })
86
+ assert(isTypedBlobRef(res.body.blob))
81
87
  smallBlob = res.body.blob
82
88
 
83
89
  const found = await aliceDb.db
@@ -123,7 +129,7 @@ describe('file uploads', () => {
123
129
  expect(uint8arrays.equals(smallFile, body)).toBeTruthy()
124
130
  })
125
131
 
126
- let largeBlob: BlobRef
132
+ let largeBlob: TypedBlobRef
127
133
  let largeFile: Uint8Array
128
134
 
129
135
  it('does not allow referencing a file that is outside blob constraints', async () => {
@@ -132,6 +138,7 @@ describe('file uploads', () => {
132
138
  headers: sc.getHeaders(alice),
133
139
  encoding: 'image/jpeg',
134
140
  })
141
+ assert(isTypedBlobRef(res.body.blob))
135
142
  largeBlob = res.body.blob
136
143
 
137
144
  const profilePromise = sc.updateProfile(alice, {
@@ -195,7 +202,7 @@ describe('file uploads', () => {
195
202
  const blob = await aliceDb.db
196
203
  .selectFrom('blob')
197
204
  .selectAll()
198
- .where('cid', '=', uploadAfterPermanent.blob.ref.toString())
205
+ .where('cid', '=', getBlobCidString(uploadAfterPermanent.blob))
199
206
  .executeTakeFirstOrThrow()
200
207
  expect(blob.tempKey).toEqual(null)
201
208
  })
@@ -208,6 +215,7 @@ describe('file uploads', () => {
208
215
  'content-encoding': 'gzip',
209
216
  },
210
217
  })
218
+ assert(isTypedBlobRef(uploaded.blob))
211
219
  expect(uploaded.blob.ref.equals(smallBlob.ref)).toBeTruthy()
212
220
  })
213
221
 
@@ -221,7 +229,7 @@ describe('file uploads', () => {
221
229
  const found = await aliceDb.db
222
230
  .selectFrom('blob')
223
231
  .selectAll()
224
- .where('cid', '=', res.body.blob.ref.toString())
232
+ .where('cid', '=', getBlobCidString(res.body.blob))
225
233
  .executeTakeFirst()
226
234
 
227
235
  expect(found?.mimeType).toBe('image/jpeg')
@@ -237,7 +245,7 @@ describe('file uploads', () => {
237
245
  const found = await aliceDb.db
238
246
  .selectFrom('blob')
239
247
  .selectAll()
240
- .where('cid', '=', res.body.blob.ref.toString())
248
+ .where('cid', '=', getBlobCidString(res.body.blob))
241
249
  .executeTakeFirst()
242
250
 
243
251
  expect(found?.mimeType).toBe('image/png')
@@ -253,7 +261,7 @@ describe('file uploads', () => {
253
261
  const found = await aliceDb.db
254
262
  .selectFrom('blob')
255
263
  .selectAll()
256
- .where('cid', '=', res.body.blob.ref.toString())
264
+ .where('cid', '=', getBlobCidString(res.body.blob))
257
265
  .executeTakeFirst()
258
266
 
259
267
  expect(found?.mimeType).toBe('test/fake')
@@ -269,7 +277,7 @@ describe('file uploads', () => {
269
277
  const found = await aliceDb.db
270
278
  .selectFrom('blob')
271
279
  .selectAll()
272
- .where('cid', '=', res.body.blob.ref.toString())
280
+ .where('cid', '=', getBlobCidString(res.body.blob))
273
281
  .executeTakeFirst()
274
282
 
275
283
  expect(found?.mimeType).toBe('text/plain')
@@ -285,7 +293,7 @@ describe('file uploads', () => {
285
293
  const found = await aliceDb.db
286
294
  .selectFrom('blob')
287
295
  .selectAll()
288
- .where('cid', '=', res.body.blob.ref.toString())
296
+ .where('cid', '=', getBlobCidString(res.body.blob))
289
297
  .executeTakeFirst()
290
298
 
291
299
  expect(found?.mimeType).toBe('application/json')
@@ -1,14 +1,9 @@
1
1
  import { once } from 'node:events'
2
- import {
3
- IncomingMessage,
4
- Server,
5
- ServerResponse,
6
- createServer,
7
- } from 'node:http'
2
+ import { Server, createServer } from 'node:http'
8
3
  import { AddressInfo } from 'node:net'
9
4
  import { type Browser, launch } from 'puppeteer'
10
5
  import { TestNetworkNoAppView } from '@atproto/dev-env'
11
- import files from '@atproto/oauth-client-browser-example' with { type: 'json' }
6
+ import { oauthClientAssetsMiddleware } from './_oauth_client_assets_middleware.js'
12
7
  import { PageHelper } from './_puppeteer.js'
13
8
 
14
9
  describe('oauth', () => {
@@ -44,7 +39,7 @@ describe('oauth', () => {
44
39
  password: 'alice-pass',
45
40
  })
46
41
 
47
- server = createServer(clientHandler)
42
+ server = createServer(oauthClientAssetsMiddleware)
48
43
  server.listen(0)
49
44
  await once(server, 'listening')
50
45
 
@@ -67,26 +62,24 @@ describe('oauth', () => {
67
62
 
68
63
  // This uses prompt=create under the hood:
69
64
  it('Allows to sign-up through OAuth', async () => {
70
- const page = await PageHelper.from(browser, { languages })
65
+ await using page = await PageHelper.from(browser, { languages })
71
66
 
72
67
  await page.goto(appUrl)
73
68
 
74
- await page.checkTitle('OAuth Client Example')
69
+ await page.assertTitle('OAuth Client Example')
75
70
 
76
- await page.navigationAction(async () => {
77
- await page.clickOnButton(`Sign up with ${new URL(network.pds.url).host}`)
78
- })
71
+ await page.navigationClick(`Sign up with ${new URL(network.pds.url).host}`)
79
72
 
80
- await page.checkTitle('Créer un compte')
73
+ await page.assertTitle("S'inscrire")
81
74
 
82
75
  await page.typeInInput('handle', 'bob')
83
76
 
84
- await page.clickOnButton('Suivant')
77
+ await page.clickOnText('Suivant')
85
78
 
86
79
  await page.typeInInput('email', 'bob@test.com')
87
80
  await page.typeInInput('password', 'bob-pass')
88
81
 
89
- await page.clickOnButton("S'inscrire")
82
+ await page.clickOnText("S'inscrire")
90
83
 
91
84
  await page.ensureTextVisibility(
92
85
  `L'application demande un contrôle total sur votre identité, ce qui signifie qu'elle pourrait casser de façon permanente, ou même usurper, votre compte. N'authorisez l'accès qu'aux applications auxquelles vous faites vraiment confiance.`,
@@ -96,54 +89,38 @@ describe('oauth', () => {
96
89
  // the client to resolve the account's did
97
90
  await network.processAll()
98
91
 
99
- await page.navigationAction(async () => {
100
- await page.clickOnButton("Authoriser l'accès")
101
- })
92
+ await page.navigationClick('Autoriser')
102
93
 
103
- await page.checkTitle('OAuth Client Example')
94
+ await page.assertTitle('OAuth Client Example')
104
95
 
105
96
  await page.ensureTextVisibility('Token info', 'h2')
106
97
 
107
- await page.clickOn('button[aria-label="User menu"]')
98
+ await page.clickOnAriaLabel('User menu')
108
99
 
109
- await page.clickOnButton('Sign out')
100
+ await page.clickOnText('Sign out')
110
101
 
111
102
  await page.waitForNetworkIdle()
112
-
113
- // TODO: Find out why we can't use "using" here
114
- await page[Symbol.asyncDispose]()
115
103
  })
116
104
 
117
- it('Allows login or signup through OAuth via a choice', async () => {
118
- const page = await PageHelper.from(browser, { languages })
105
+ it('Allows canceling the OAuth flow', async () => {
106
+ await using page = await PageHelper.from(browser, { languages })
119
107
 
120
108
  await page.goto(appUrl)
121
109
 
122
- await page.checkTitle('OAuth Client Example')
123
-
124
- await page.navigationAction(async () => {
125
- await page.clickOnButton(`Login with ${new URL(network.pds.url).host}`)
126
- })
110
+ await page.assertTitle('OAuth Client Example')
127
111
 
128
- await page.checkTitle('Authentification')
112
+ await page.navigationClick(`Login with ${new URL(network.pds.url).host}`)
129
113
 
130
- await page.ensureTextVisibility('Annuler', 'button')
131
- await page.ensureTextVisibility('Se connecter', 'button')
132
- await page.ensureTextVisibility('Créer un nouveau compte', 'button')
114
+ await page.assertTitle("S'identifier")
133
115
 
134
116
  // Cancel the OAuth flow:
135
- await page.navigationAction(async () => {
136
- await page.clickOnButton('Annuler')
137
- })
117
+ await page.navigationClick('Annuler')
138
118
 
139
- await page.checkTitle('OAuth Client Example')
119
+ await page.assertTitle('OAuth Client Example')
140
120
 
141
121
  await page.ensureTextVisibility('Login with the Atmosphere', 'h2')
142
122
 
143
123
  await page.waitForNetworkIdle()
144
-
145
- // TODO: Find out why we can't use "using" here
146
- await page[Symbol.asyncDispose]()
147
124
  })
148
125
 
149
126
  it('allows resetting the password', async () => {
@@ -153,31 +130,29 @@ describe('oauth', () => {
153
130
  // noop
154
131
  })
155
132
 
156
- const page = await PageHelper.from(browser, { languages })
133
+ await using page = await PageHelper.from(browser, { languages })
157
134
 
158
135
  await page.goto(appUrl)
159
136
 
160
- await page.checkTitle('OAuth Client Example')
137
+ await page.assertTitle('OAuth Client Example')
161
138
 
162
- await page.navigationAction(async () => {
163
- const input = await page.typeIn('input[name="identifier"]', 'alice.test')
139
+ const input = await page.typeInInput('identifier', 'alice.test')
164
140
 
165
- await input.press('Enter')
166
- })
141
+ await page.navigationAction(async () => input.press('Enter'))
167
142
 
168
- await page.checkTitle('Connexion')
143
+ await page.assertTitle('Connexion')
169
144
 
170
- await page.clickOnButton('Oublié ?')
145
+ await page.clickOnText('Oublié ?')
171
146
 
172
- await page.checkTitle('Mot de passe oublié')
147
+ await page.assertTitle('Mot de passe oublié')
173
148
 
174
149
  await page.typeInInput('email', 'alice@test.com')
175
150
 
176
151
  expect(sendTemplateMock).toHaveBeenCalledTimes(0)
177
152
 
178
- await page.clickOnButton('Suivant')
153
+ await page.clickOnText('Suivant')
179
154
 
180
- await page.checkTitle('Réinitialiser le mot de passe')
155
+ await page.assertTitle('Réinitialiser le mot de passe')
181
156
 
182
157
  expect(sendTemplateMock).toHaveBeenCalledTimes(1)
183
158
 
@@ -191,34 +166,29 @@ describe('oauth', () => {
191
166
 
192
167
  await page.typeInInput('password', 'alice-new-pass')
193
168
 
194
- await page.clickOnButton('Suivant')
169
+ await page.clickOnText('Suivant')
195
170
 
196
- await page.checkTitle('Mot de passe mis à jour')
171
+ await page.assertTitle('Mot de passe mis à jour')
197
172
 
198
173
  await page.ensureTextVisibility('Mot de passe mis à jour !', 'h2')
199
174
 
200
- // TODO: Find out why we can't use "using" here
201
- await page[Symbol.asyncDispose]()
202
-
203
175
  sendTemplateMock.mockRestore()
204
176
  })
205
177
 
206
178
  it('Allows to sign-in through OAuth', async () => {
207
- const page = await PageHelper.from(browser, { languages })
179
+ await using page = await PageHelper.from(browser, { languages })
208
180
 
209
181
  await page.goto(appUrl)
210
182
 
211
- await page.checkTitle('OAuth Client Example')
183
+ await page.assertTitle('OAuth Client Example')
212
184
 
213
- await page.navigationAction(async () => {
214
- const input = await page.typeIn('input[name="identifier"]', 'alice.test')
185
+ const input = await page.typeInInput('identifier', 'alice.test')
215
186
 
216
- await input.press('Enter')
217
- })
187
+ await page.navigationAction(async () => input.press('Enter'))
218
188
 
219
- await page.checkTitle('Connexion')
189
+ await page.assertTitle('Connexion')
220
190
 
221
- await page.typeIn('input[type="password"]', 'alice-new-pass')
191
+ await page.typeInInput('password', 'alice-new-pass')
222
192
 
223
193
  // Make sure the warning is visible
224
194
  await page.ensureTextVisibility('Avertissement', 'h3')
@@ -227,79 +197,46 @@ describe('oauth', () => {
227
197
  'label::-p-text(Se souvenir de ce compte sur cet appareil)',
228
198
  )
229
199
 
230
- await page.clickOnButton('Se connecter')
200
+ await page.clickOnText('Se connecter')
231
201
 
232
- await page.checkTitle("Authoriser l'accès")
202
+ await page.assertTitle('Autoriser')
233
203
 
234
- await page.navigationAction(async () => {
235
- await page.clickOnButton("Authoriser l'accès")
236
- })
204
+ await page.navigationClick('Autoriser')
237
205
 
238
- await page.checkTitle('OAuth Client Example')
206
+ await page.assertTitle('OAuth Client Example')
239
207
 
240
208
  await page.ensureTextVisibility('Token info', 'h2')
241
209
 
242
- await page.clickOn('button[aria-label="User menu"]')
210
+ await page.clickOnAriaLabel('User menu')
243
211
 
244
- await page.clickOnButton('Sign out')
212
+ await page.clickOnText('Sign out')
245
213
 
246
214
  await page.waitForNetworkIdle()
247
-
248
- // TODO: Find out why we can't use "using" here
249
- await page[Symbol.asyncDispose]()
250
215
  })
251
216
 
252
217
  it('remembers the session', async () => {
253
- const page = await PageHelper.from(browser, { languages })
218
+ await using page = await PageHelper.from(browser, { languages })
254
219
 
255
220
  await page.goto(appUrl)
256
221
 
257
- await page.checkTitle('OAuth Client Example')
222
+ await page.assertTitle('OAuth Client Example')
258
223
 
259
- await page.navigationAction(async () => {
260
- const input = await page.typeIn('input[name="identifier"]', 'alice.test')
224
+ const input = await page.typeInInput('identifier', 'alice.test')
261
225
 
262
- await input.press('Enter')
263
- })
226
+ await page.navigationAction(async () => input.press('Enter'))
264
227
 
265
- await page.checkTitle("Authoriser l'accès")
228
+ await page.assertTitle('Autoriser')
266
229
 
267
- await page.navigationAction(async () => {
268
- await page.clickOnButton("Authoriser l'accès")
269
- })
230
+ await page.navigationClick('Autoriser')
270
231
 
271
- await page.checkTitle('OAuth Client Example')
232
+ await page.assertTitle('OAuth Client Example')
272
233
 
273
234
  await page.ensureTextVisibility('Token info', 'h2')
274
235
 
275
- await page.clickOn('button[aria-label="User menu"]')
236
+ await page.clickOnAriaLabel('User menu')
276
237
 
277
- await page.clickOnButton('Sign out')
238
+ await page.clickOnText('Sign out')
278
239
 
279
240
  await page.waitForNetworkIdle()
280
-
281
- // TODO: Find out why we can't use "using" here
282
- await page[Symbol.asyncDispose]()
283
241
  })
284
242
  })
285
-
286
- function clientHandler(
287
- req: IncomingMessage,
288
- res: ServerResponse,
289
- next?: (err?: unknown) => void,
290
- ): void {
291
- const path = req.url?.split('?')[0].slice(1) || 'index.html'
292
- const file = Object.hasOwn(files, path) ? files[path] : null
293
-
294
- if (file) {
295
- res
296
- .writeHead(200, 'OK', { 'content-type': file.mime })
297
- .end(Buffer.from(file.data, 'base64'))
298
- } else if (next) {
299
- next()
300
- } else {
301
- res
302
- .writeHead(404, 'Not Found', { 'content-type': 'text/plain' })
303
- .end('Page not found')
304
- }
305
- }