@api-client/core 0.14.9 → 0.15.0

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 (110) hide show
  1. package/RELEASE.md +163 -0
  2. package/RELEASE_SETUP.md +235 -0
  3. package/build/src/events/authorization/AuthorizationEvents.d.ts +1 -1
  4. package/build/src/events/authorization/AuthorizationEvents.d.ts.map +1 -1
  5. package/build/src/events/authorization/AuthorizationEvents.js +1 -1
  6. package/build/src/events/authorization/AuthorizationEvents.js.map +1 -1
  7. package/build/src/events/cookies/CookieEvents.d.ts +1 -1
  8. package/build/src/events/cookies/CookieEvents.d.ts.map +1 -1
  9. package/build/src/events/cookies/CookieEvents.js +1 -1
  10. package/build/src/events/cookies/CookieEvents.js.map +1 -1
  11. package/build/src/modeling/DataDomain.d.ts +45 -3
  12. package/build/src/modeling/DataDomain.d.ts.map +1 -1
  13. package/build/src/modeling/DataDomain.js +81 -3
  14. package/build/src/modeling/DataDomain.js.map +1 -1
  15. package/build/src/modeling/DomainElement.d.ts +7 -0
  16. package/build/src/modeling/DomainElement.d.ts.map +1 -1
  17. package/build/src/modeling/DomainElement.js +14 -0
  18. package/build/src/modeling/DomainElement.js.map +1 -1
  19. package/build/src/modeling/DomainImpactAnalysis.d.ts +22 -119
  20. package/build/src/modeling/DomainImpactAnalysis.d.ts.map +1 -1
  21. package/build/src/modeling/DomainImpactAnalysis.js +49 -155
  22. package/build/src/modeling/DomainImpactAnalysis.js.map +1 -1
  23. package/build/src/modeling/DomainValidation.d.ts +8 -0
  24. package/build/src/modeling/DomainValidation.d.ts.map +1 -0
  25. package/build/src/modeling/DomainValidation.js +99 -0
  26. package/build/src/modeling/DomainValidation.js.map +1 -0
  27. package/build/src/modeling/types.d.ts +70 -0
  28. package/build/src/modeling/types.d.ts.map +1 -1
  29. package/build/src/modeling/types.js.map +1 -1
  30. package/build/src/modeling/validation/entity_validation.js +1 -1
  31. package/build/src/modeling/validation/entity_validation.js.map +1 -1
  32. package/build/src/modeling/validation/rules.d.ts +2 -3
  33. package/build/src/modeling/validation/rules.d.ts.map +1 -1
  34. package/build/src/modeling/validation/rules.js.map +1 -1
  35. package/build/src/modeling/validation/semantic_validation.d.ts +31 -0
  36. package/build/src/modeling/validation/semantic_validation.d.ts.map +1 -0
  37. package/build/src/modeling/validation/semantic_validation.js +126 -0
  38. package/build/src/modeling/validation/semantic_validation.js.map +1 -0
  39. package/build/tsconfig.tsbuildinfo +1 -1
  40. package/data/models/example-generator-api.json +12 -12
  41. package/noop.ts +3 -0
  42. package/package.json +9 -4
  43. package/src/events/authorization/AuthorizationEvents.ts +1 -1
  44. package/src/events/cookies/CookieEvents.ts +1 -1
  45. package/src/modeling/DataDomain.ts +84 -3
  46. package/src/modeling/DomainElement.ts +16 -0
  47. package/src/modeling/DomainImpactAnalysis.ts +54 -239
  48. package/src/modeling/DomainValidation.ts +105 -0
  49. package/src/modeling/types.ts +86 -0
  50. package/src/modeling/validation/entity_validation.ts +1 -1
  51. package/src/modeling/validation/rules.ts +2 -4
  52. package/src/modeling/validation/semantic_validation.ts +145 -0
  53. package/tests/unit/events/EventsTestHelpers.ts +16 -0
  54. package/tests/unit/events/amf.spec.ts +151 -0
  55. package/tests/unit/events/authorization.spec.ts +150 -0
  56. package/tests/unit/events/cookie.spec.ts +274 -0
  57. package/tests/unit/events/encryption.spec.ts +108 -0
  58. package/tests/unit/events/events_polyfills.ts +77 -0
  59. package/tests/unit/events/process.spec.ts +120 -0
  60. package/tests/unit/events/reporting.spec.ts +82 -0
  61. package/tests/unit/events/telemetry.spec.ts +224 -0
  62. package/tests/unit/events/transport.spec.ts +139 -0
  63. package/tests/unit/modeling/data_domain_foreign.spec.ts +244 -0
  64. package/tests/unit/modeling/domain_impact_analysis.spec.ts +0 -110
  65. package/tests/unit/modeling/domain_validation.spec.ts +94 -0
  66. package/tests/unit/modeling/validation/semantic_validation.spec.ts +91 -0
  67. package/tests/unit/models/environment.spec.ts +574 -0
  68. package/tests/unit/models/error_response.spec.ts +183 -0
  69. package/tests/unit/models/headers_array.spec.ts +86 -0
  70. package/tests/unit/models/http-actions/assertion/equal_assertion.spec.ts +103 -0
  71. package/tests/unit/models/http-actions/assertion/greater_than_assertion.spec.ts +91 -0
  72. package/tests/unit/models/http-actions/assertion/includes_assertion.spec.ts +71 -0
  73. package/tests/unit/models/http-actions/assertion/less_than_assertion.spec.ts +91 -0
  74. package/tests/unit/models/http-actions/assertion/matches_assertion.spec.ts +71 -0
  75. package/tests/unit/models/http-actions/assertion/matches_schema_assertion.spec.ts +117 -0
  76. package/tests/unit/models/http-actions/assertion/not_equal_assertion.spec.ts +103 -0
  77. package/tests/unit/models/http-actions/assertion/not_includes_assertion.spec.ts +71 -0
  78. package/tests/unit/models/http-actions/assertion/not_ok_assertion.spec.ts +47 -0
  79. package/tests/unit/models/http-actions/assertion/not_to_be_assertion.spec.ts +72 -0
  80. package/tests/unit/models/http-actions/assertion/ok_assertion.spec.ts +44 -0
  81. package/tests/unit/models/http-actions/assertion/to_be_assertion.spec.ts +71 -0
  82. package/tests/unit/models/http-actions/transformation/as_lower_case_step.spec.ts +47 -0
  83. package/tests/unit/models/http-actions/transformation/as_number_step.spec.ts +47 -0
  84. package/tests/unit/models/http-actions/transformation/as_upper_case_step.spec.ts +47 -0
  85. package/tests/unit/models/http-actions/transformation/round_step.spec.ts +69 -0
  86. package/tests/unit/models/http-actions/transformation/substring_step.spec.ts +85 -0
  87. package/tests/unit/models/http-actions/transformation/trim_step.spec.ts +44 -0
  88. package/tests/unit/models/http_cookie.spec.ts +516 -0
  89. package/tests/unit/models/http_history.spec.ts +443 -0
  90. package/tests/unit/models/project_folder.spec.ts +926 -0
  91. package/tests/unit/models/project_item.spec.ts +137 -0
  92. package/tests/unit/models/project_request.spec.ts +1047 -0
  93. package/tests/unit/models/project_schema.spec.ts +236 -0
  94. package/tests/unit/models/property.spec.ts +625 -0
  95. package/tests/unit/models/provider.spec.ts +102 -0
  96. package/tests/unit/models/request.spec.ts +1206 -0
  97. package/tests/unit/models/request_log.spec.ts +308 -0
  98. package/tests/unit/models/request_time.spec.ts +138 -0
  99. package/tests/unit/models/response_redirect.spec.ts +303 -0
  100. package/tests/unit/models/sent_request.spec.ts +206 -0
  101. package/tests/unit/models/server.spec.ts +195 -0
  102. package/tests/unit/models/thing.spec.ts +154 -0
  103. package/build/oauth-popup.html +0 -33
  104. /package/tests/unit/models/{Certificate.spec.ts → certificate.spec.ts} +0 -0
  105. /package/tests/unit/models/{HostRule.spec.ts → host_rule.spec.ts} +0 -0
  106. /package/tests/unit/models/{HttpProject.spec.ts → http_project.spec.ts} +0 -0
  107. /package/tests/unit/models/{HttpRequest.spec.ts → http_request.spec.ts} +0 -0
  108. /package/tests/unit/models/{HttpResponse.spec.ts → http_response.spec.ts} +0 -0
  109. /package/tests/unit/models/{License.spec.ts → license.spec.ts} +0 -0
  110. /package/tests/unit/models/{Response.spec.ts → response.spec.ts} +0 -0
@@ -5,6 +5,14 @@ import type { DomainEntity } from './DomainEntity.js'
5
5
  import type { DomainModel } from './DomainModel.js'
6
6
  import type { DomainNamespace } from './DomainNamespace.js'
7
7
  import type { DomainProperty } from './DomainProperty.js'
8
+ import type {
9
+ DomainNamespaceKind,
10
+ DomainEntityKind,
11
+ DomainModelKind,
12
+ DomainPropertyKind,
13
+ DomainAssociationKind,
14
+ DataDomainKind,
15
+ } from '../models/kinds.js'
8
16
 
9
17
  export interface DataDomainRemoveOptions {
10
18
  /**
@@ -732,3 +740,81 @@ export interface RateLimitRule {
732
740
  */
733
741
  burst: number
734
742
  }
743
+
744
+ export type DomainImpactKinds =
745
+ | typeof DomainNamespaceKind
746
+ | typeof DomainEntityKind
747
+ | typeof DomainModelKind
748
+ | typeof DomainPropertyKind
749
+ | typeof DomainAssociationKind
750
+ | typeof DataDomainKind
751
+
752
+ /**
753
+ * The impact analysis report
754
+ */
755
+ export interface DomainImpactReport {
756
+ /**
757
+ * The key of the impacted data object.
758
+ * This is the key of the object that is being changed.
759
+ */
760
+ key: string
761
+ /**
762
+ * The kind of the impacted data object.
763
+ * This is the kind of the object that is being changed.
764
+ */
765
+ kind: DomainImpactKinds
766
+ /**
767
+ * The list of impacted data objects.
768
+ */
769
+ impact: DomainImpactItem[]
770
+ /**
771
+ * Whether it is possible to proceed with the change.
772
+ * If the change is not possible, the reason will be in the impact list.
773
+ */
774
+ canProceed: boolean
775
+ }
776
+
777
+ export interface DomainImpactItem {
778
+ /**
779
+ * The key of the impacted data object.
780
+ */
781
+ key: string
782
+ /**
783
+ * The kind of the impacted data object.
784
+ */
785
+ kind: string
786
+ /**
787
+ * The type of the impact.
788
+ *
789
+ * - `delete` - The data object would be deleted.
790
+ */
791
+ type: 'delete' | 'publish'
792
+ /**
793
+ * The impact description.
794
+ * Explains what will happen to the impacted data object.
795
+ * This is a human-readable description of the impact.
796
+ * It should be clear and concise.
797
+ */
798
+ impact: string
799
+ /**
800
+ * The severity of the impact.
801
+ *
802
+ * - `info` - The impact is informational.
803
+ * - `warning` - The impact can potentially cause problems but is not a blocker.
804
+ * - `error` - The impact is a blocker and needs to be resolved before proceeding.
805
+ */
806
+ severity: 'info' | 'warning' | 'error'
807
+ /**
808
+ * The type of the relationship between two impacted objects.
809
+ */
810
+ relationship?: 'child'
811
+ /**
812
+ * The resolution of the conflict if the change will be forced.
813
+ */
814
+ resolution?: string
815
+ /**
816
+ * The optional parent of the impacted data object.
817
+ * For example, if the impacted item is a property, this will be the entity it belongs to.
818
+ */
819
+ parent?: string
820
+ }
@@ -225,7 +225,7 @@ export class EntityValidation {
225
225
  })
226
226
  }
227
227
  }
228
- for (const other of this.domain.listForeignEntities()) {
228
+ for (const other of this.domain.listAllForeignEntities()) {
229
229
  if (other.info.name?.toLowerCase() === name && other.key !== entity.key) {
230
230
  const message = `The "${name}" entity name is already used in the foreign data domain.`
231
231
  const help = `The name must be unique. This includes references to other data domains.`
@@ -1,7 +1,7 @@
1
1
  import { ReservedKeywords } from './postgresql.js'
2
2
  import type { DomainProperty } from '../DomainProperty.js'
3
3
  import type { DomainAssociation } from '../DomainAssociation.js'
4
- import type { DomainImpactItem } from '../DomainImpactAnalysis.js'
4
+ import type { DomainImpactItem } from '../types.js'
5
5
  import type { DomainEntity } from '../DomainEntity.js'
6
6
  import { DomainPropertyKind } from '../../models/kinds.js'
7
7
 
@@ -10,11 +10,9 @@ import { DomainPropertyKind } from '../../models/kinds.js'
10
10
  * - `impact` -> `message`
11
11
  * - `resolution` -> `help`
12
12
  * - `type` -> unused
13
- * - `blocking` -> unused (deprecated)
14
13
  * - `relationship` -> unused
15
14
  */
16
- export interface DomainValidation
17
- extends Omit<DomainImpactItem, 'type' | 'impact' | 'blocking' | 'relationship' | 'resolution'> {
15
+ export interface DomainValidation extends Omit<DomainImpactItem, 'type' | 'impact' | 'relationship' | 'resolution'> {
18
16
  /**
19
17
  * The field that did not pass validation.
20
18
  */
@@ -0,0 +1,145 @@
1
+ import { DomainEntityKind } from '../../models/kinds.js'
2
+ import type { DataDomain } from '../DataDomain.js'
3
+ import { SemanticType } from '../Semantics.js'
4
+ import type { DomainValidation } from './rules.js'
5
+
6
+ /**
7
+ * SemanticValidation is a class that performs validation on semantics in a data domain.
8
+ * It checks for required and recommended semantics in entities, properties, and associations.
9
+ */
10
+ export class SemanticValidation {
11
+ constructor(protected domain: DataDomain) {}
12
+
13
+ /**
14
+ * Performs all the semantic validation rules on the domain.
15
+ * @returns The list of validation messages.
16
+ */
17
+ validate(): DomainValidation[] {
18
+ const results: DomainValidation[] = []
19
+
20
+ // Check for User entity semantic
21
+ const hasUserEntity = this.validateUserEntity()
22
+ results.push(...hasUserEntity)
23
+
24
+ // Check for timestamp semantics
25
+ const timestampSemantics = this.validateTimestampSemantics()
26
+ results.push(...timestampSemantics)
27
+
28
+ // Check for soft delete semantics
29
+ const softDeleteSemantics = this.validateSoftDeleteSemantics()
30
+ results.push(...softDeleteSemantics)
31
+
32
+ return results
33
+ }
34
+
35
+ /**
36
+ * Validates if there is at least one entity with the User semantic.
37
+ * This is a recommended semantic for authentication purposes.
38
+ */
39
+ private validateUserEntity(): DomainValidation[] {
40
+ const results: DomainValidation[] = []
41
+ let hasUserEntity = false
42
+
43
+ for (const entity of this.domain.listEntities()) {
44
+ if (entity.hasSemantic(SemanticType.User)) {
45
+ hasUserEntity = true
46
+ break
47
+ }
48
+ }
49
+
50
+ if (!hasUserEntity) {
51
+ results.push({
52
+ field: 'semantics',
53
+ rule: 'recommended',
54
+ message: 'No entity with User taxonomy found in the domain.',
55
+ help: 'It is recommended to have at least one entity with the User taxonomy for authentication purposes.',
56
+ severity: 'warning',
57
+ key: 'user_semantic',
58
+ kind: DomainEntityKind,
59
+ })
60
+ }
61
+
62
+ return results
63
+ }
64
+
65
+ /**
66
+ * Validates if entities have the recommended timestamp semantics.
67
+ * This includes CreatedTimestamp and UpdatedTimestamp.
68
+ */
69
+ private validateTimestampSemantics(): DomainValidation[] {
70
+ const results: DomainValidation[] = []
71
+
72
+ for (const entity of this.domain.listEntities()) {
73
+ let hasCreatedTimestamp = false
74
+ let hasUpdatedTimestamp = false
75
+
76
+ for (const property of entity.listProperties()) {
77
+ if (property.hasSemantic(SemanticType.CreatedTimestamp)) {
78
+ hasCreatedTimestamp = true
79
+ }
80
+ if (property.hasSemantic(SemanticType.UpdatedTimestamp)) {
81
+ hasUpdatedTimestamp = true
82
+ }
83
+ }
84
+
85
+ if (!hasCreatedTimestamp) {
86
+ results.push({
87
+ field: 'semantics',
88
+ rule: 'recommended',
89
+ message: `The "${entity.info.getLabel()}" entity does not have a CreatedTimestamp taxonomy.`,
90
+ help: 'It is recommended to have a CreatedTimestamp property to track when the entity was created.',
91
+ severity: 'info',
92
+ key: entity.key,
93
+ kind: entity.kind,
94
+ })
95
+ }
96
+
97
+ if (!hasUpdatedTimestamp) {
98
+ results.push({
99
+ field: 'semantics',
100
+ rule: 'recommended',
101
+ message: `The "${entity.info.getLabel()}" entity does not have an UpdatedTimestamp taxonomy.`,
102
+ help: 'It is recommended to have an UpdatedTimestamp property to track when the entity was last modified.',
103
+ severity: 'info',
104
+ key: entity.key,
105
+ kind: entity.kind,
106
+ })
107
+ }
108
+ }
109
+
110
+ return results
111
+ }
112
+
113
+ /**
114
+ * Validates if entities have the recommended soft delete semantics.
115
+ * This includes either DeletedTimestamp or DeletedFlag.
116
+ */
117
+ private validateSoftDeleteSemantics(): DomainValidation[] {
118
+ const results: DomainValidation[] = []
119
+
120
+ for (const entity of this.domain.listEntities()) {
121
+ let hasSoftDelete = false
122
+
123
+ for (const property of entity.listProperties()) {
124
+ if (property.hasSemantic(SemanticType.DeletedTimestamp) || property.hasSemantic(SemanticType.DeletedFlag)) {
125
+ hasSoftDelete = true
126
+ break
127
+ }
128
+ }
129
+
130
+ if (!hasSoftDelete) {
131
+ results.push({
132
+ field: 'semantics',
133
+ rule: 'recommended',
134
+ message: `The "${entity.info.getLabel()}" entity does not have soft delete taxonomy.`,
135
+ help: 'It is recommended to have either a DeletedTimestamp or DeletedFlag property for soft deletion.',
136
+ severity: 'info',
137
+ key: entity.key,
138
+ kind: entity.kind,
139
+ })
140
+ }
141
+ }
142
+
143
+ return results
144
+ }
145
+ }
@@ -0,0 +1,16 @@
1
+ // The event names should be unique across all events in all modules.
2
+ const names: string[] = []
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ export function ensureUnique(namespace: string, src: any): void {
6
+ for (const [key, value] of Object.entries(src)) {
7
+ const typedValue = value as string
8
+ if (typeof typedValue !== 'string') {
9
+ return
10
+ }
11
+ if (names.includes(typedValue)) {
12
+ throw new Error(`${namespace}.${key} has duplicated event name ${typedValue}`)
13
+ }
14
+ names.push(typedValue)
15
+ }
16
+ }
@@ -0,0 +1,151 @@
1
+ import { test } from '@japa/runner'
2
+ import sinon from 'sinon'
3
+ import { EventTypes } from '../../../src/events/EventTypes.js'
4
+ import { Events } from '../../../src/events/Events.js'
5
+ import { ensureUnique } from './EventsTestHelpers.js'
6
+ import './events_polyfills.js'
7
+
8
+ test.group('EventTypes.Amf', () => {
9
+ test('has the namespace', ({ assert }) => {
10
+ assert.typeOf(EventTypes.Amf, 'object')
11
+ })
12
+
13
+ test('has frozen namespace', ({ assert }) => {
14
+ assert.throws(() => {
15
+ // @ts-expect-error Used in testing
16
+ EventTypes.Amf = { read: '' }
17
+ })
18
+ })
19
+
20
+ test('has "{prop}" property')
21
+ .with([
22
+ {
23
+ prop: 'processApiLink',
24
+ value: 'amfprocessapilink',
25
+ },
26
+ {
27
+ prop: 'processBuffer',
28
+ value: 'amfprocessbuffer',
29
+ },
30
+ {
31
+ prop: 'processApiFile',
32
+ value: 'amfprocessapifile',
33
+ },
34
+ {
35
+ prop: 'selectApiMainFile',
36
+ value: 'amfselectapimainfile',
37
+ },
38
+ ])
39
+ .run(({ assert }, { prop, value }) => {
40
+ // @ts-expect-error Used in testing
41
+ assert.equal(EventTypes.Amf[prop], value)
42
+ })
43
+
44
+ test('has unique events on the namespace', () => {
45
+ ensureUnique('EventTypes.Amf', EventTypes.Amf)
46
+ })
47
+ })
48
+
49
+ test.group('processApiLink()', () => {
50
+ const url = 'https://api.com'
51
+ const mainFile = 'api.raml'
52
+ const md5 = '123qwe'
53
+ const packaging = 'zip'
54
+
55
+ test('dispatches the event', async ({ assert }) => {
56
+ const spy = sinon.spy()
57
+ globalThis.addEventListener(EventTypes.Amf.processApiLink, spy)
58
+ await Events.Amf.processApiLink(url)
59
+ assert.isTrue(spy.calledOnce)
60
+ })
61
+
62
+ test('sets the arguments on the detail object', async ({ assert }) => {
63
+ const spy = sinon.spy()
64
+ globalThis.addEventListener(EventTypes.Amf.processApiLink, spy)
65
+ await Events.Amf.processApiLink(url, mainFile, md5, packaging)
66
+ globalThis.removeEventListener(EventTypes.Amf.processApiLink, spy)
67
+ const { detail } = spy.args[0][0]
68
+ assert.equal(detail.url, url, 'url is set')
69
+ assert.equal(detail.mainFile, mainFile, 'mainFile is set')
70
+ assert.equal(detail.md5, md5, 'md5 is set')
71
+ assert.equal(detail.packaging, packaging, 'packaging is set')
72
+ })
73
+
74
+ test('returns the result', async ({ assert }) => {
75
+ const parseResult = { model: '', type: { type: 'RAML 1.0' } }
76
+ function handler(e: CustomEvent): void {
77
+ globalThis.removeEventListener(EventTypes.Amf.processApiLink, handler as EventListener)
78
+ e.preventDefault()
79
+ e.detail.result = Promise.resolve(parseResult)
80
+ }
81
+ globalThis.addEventListener(EventTypes.Amf.processApiLink, handler as EventListener)
82
+ const result = await Events.Amf.processApiLink(url)
83
+ assert.equal(result, parseResult)
84
+ })
85
+ })
86
+
87
+ test.group('processApiFile()', () => {
88
+ const file = new File([], 'test.txt')
89
+
90
+ test('dispatches the event', async ({ assert }) => {
91
+ const spy = sinon.spy()
92
+ globalThis.addEventListener(EventTypes.Amf.processApiFile, spy)
93
+ await Events.Amf.processApiFile(file)
94
+ globalThis.removeEventListener(EventTypes.Amf.processApiFile, spy)
95
+ assert.isTrue(spy.calledOnce)
96
+ })
97
+
98
+ test('sets the arguments on the detail object', async ({ assert }) => {
99
+ const spy = sinon.spy()
100
+ globalThis.addEventListener(EventTypes.Amf.processApiFile, spy)
101
+ await Events.Amf.processApiFile(file)
102
+ globalThis.removeEventListener(EventTypes.Amf.processApiFile, spy)
103
+ const { detail } = spy.args[0][0]
104
+ assert.deepEqual(detail.file, file, 'file is set')
105
+ })
106
+
107
+ test('returns the result', async ({ assert }) => {
108
+ const parseResult = { model: '', type: { type: 'RAML 1.0' } }
109
+ function handler(e: CustomEvent): void {
110
+ globalThis.removeEventListener(EventTypes.Amf.processApiFile, handler as EventListener)
111
+ e.preventDefault()
112
+ e.detail.result = Promise.resolve(parseResult)
113
+ }
114
+ globalThis.addEventListener(EventTypes.Amf.processApiFile, handler as EventListener)
115
+ const result = await Events.Amf.processApiFile(file)
116
+ assert.equal(result, parseResult)
117
+ })
118
+ })
119
+
120
+ test.group('selectApiMainFile()', () => {
121
+ const candidates = ['test']
122
+
123
+ test('dispatches the event', async ({ assert }) => {
124
+ const spy = sinon.spy()
125
+ globalThis.addEventListener(EventTypes.Amf.selectApiMainFile, spy)
126
+ await Events.Amf.selectApiMainFile(candidates)
127
+ globalThis.removeEventListener(EventTypes.Amf.selectApiMainFile, spy)
128
+ assert.isTrue(spy.calledOnce)
129
+ })
130
+
131
+ test('sets the arguments on the detail object', async ({ assert }) => {
132
+ const spy = sinon.spy()
133
+ globalThis.addEventListener(EventTypes.Amf.selectApiMainFile, spy)
134
+ await Events.Amf.selectApiMainFile(candidates)
135
+ globalThis.removeEventListener(EventTypes.Amf.selectApiMainFile, spy)
136
+ const { detail } = spy.args[0][0]
137
+ assert.deepEqual(detail.candidates, candidates, 'candidates is set')
138
+ })
139
+
140
+ test('returns the result', async ({ assert }) => {
141
+ const selected = 'test'
142
+ function handler(e: CustomEvent): void {
143
+ globalThis.removeEventListener(EventTypes.Amf.selectApiMainFile, handler as EventListener)
144
+ e.preventDefault()
145
+ e.detail.result = Promise.resolve(selected)
146
+ }
147
+ globalThis.addEventListener(EventTypes.Amf.selectApiMainFile, handler as EventListener)
148
+ const result = await Events.Amf.selectApiMainFile(candidates)
149
+ assert.equal(result, selected)
150
+ })
151
+ })
@@ -0,0 +1,150 @@
1
+ import { test } from '@japa/runner'
2
+ import sinon from 'sinon'
3
+ import { EventTypes } from '../../../src/events/EventTypes.js'
4
+ import { Events } from '../../../src/events/Events.js'
5
+ import { ensureUnique } from './EventsTestHelpers.js'
6
+ import './events_polyfills.js'
7
+
8
+ test.group('Events > Authorization > EventTypes.Authorization', () => {
9
+ test('has the namespace', ({ assert }) => {
10
+ assert.typeOf(EventTypes.Authorization, 'object')
11
+ })
12
+
13
+ test('has OAuth2 namespace', ({ assert }) => {
14
+ assert.typeOf(EventTypes.Authorization.OAuth2, 'object')
15
+ })
16
+
17
+ test('has frozen OAuth2 namespace', ({ assert }) => {
18
+ assert.throws(() => {
19
+ // @ts-expect-error Used in testing
20
+ EventTypes.Authorization.OAuth2 = { read: '' }
21
+ })
22
+ })
23
+
24
+ test('has "{prop}" property for OAuth2')
25
+ .with([
26
+ { prop: 'authorize', value: 'oauth2authorize' },
27
+ { prop: 'removeToken', value: 'oauth2removetoken' },
28
+ ])
29
+ .run(({ assert }, { prop, value }) => {
30
+ // @ts-expect-error Used in testing
31
+ assert.equal(EventTypes.Authorization.OAuth2[prop], value)
32
+ })
33
+
34
+ test('has unique events for OAuth2 namespace', () => {
35
+ ensureUnique('EventTypes.Authorization.OAuth2', EventTypes.Authorization.OAuth2)
36
+ })
37
+
38
+ test('has frozen Oidc namespace', ({ assert }) => {
39
+ assert.throws(() => {
40
+ // @ts-expect-error Used in testing
41
+ EventTypes.Authorization.Oidc = { read: '' }
42
+ })
43
+ })
44
+
45
+ test('has "{prop}" property for Oidc')
46
+ .with([
47
+ { prop: 'authorize', value: 'oidcauthorize' },
48
+ { prop: 'removeTokens', value: 'oidcremovetokens' },
49
+ ])
50
+ .run(({ assert }, { prop, value }) => {
51
+ // @ts-expect-error Used in testing
52
+ assert.equal(EventTypes.Authorization.Oidc[prop], value)
53
+ })
54
+
55
+ test('has unique events for Oidc namespace', () => {
56
+ ensureUnique('EventTypes.Authorization.Oidc', EventTypes.Authorization.Oidc)
57
+ })
58
+ })
59
+
60
+ test.group('Events > Authorization > Events.Authorization > OAuth2 > authorize()', () => {
61
+ const config = { responseType: 'implicit' }
62
+
63
+ test('dispatches the event', async ({ assert }) => {
64
+ const spy = sinon.spy()
65
+ globalThis.addEventListener(EventTypes.Authorization.OAuth2.authorize, spy)
66
+ Events.Authorization.OAuth2.authorize(config)
67
+ globalThis.removeEventListener(EventTypes.Authorization.OAuth2.authorize, spy)
68
+ assert.isTrue(spy.calledOnce)
69
+ })
70
+
71
+ test('has the configuration on the detail', async ({ assert }) => {
72
+ const spy = sinon.spy()
73
+ globalThis.addEventListener(EventTypes.Authorization.OAuth2.authorize, spy)
74
+ Events.Authorization.OAuth2.authorize(config)
75
+ globalThis.removeEventListener(EventTypes.Authorization.OAuth2.authorize, spy)
76
+ const e = spy.args[0][0]
77
+ const cnf = e.detail
78
+ delete cnf.result
79
+ assert.deepEqual(cnf, config)
80
+ })
81
+ })
82
+
83
+ test.group('Events > Authorization > Events.Authorization > OAuth2 > removeToken()', () => {
84
+ const config = { clientId: 'id', authorizationUri: 'test' }
85
+
86
+ test('dispatches the event', async ({ assert }) => {
87
+ const spy = sinon.spy()
88
+ globalThis.addEventListener(EventTypes.Authorization.OAuth2.removeToken, spy)
89
+ Events.Authorization.OAuth2.removeToken(config)
90
+ globalThis.removeEventListener(EventTypes.Authorization.OAuth2.removeToken, spy)
91
+ assert.isTrue(spy.calledOnce)
92
+ })
93
+
94
+ test('has the configuration on the detail', async ({ assert }) => {
95
+ const spy = sinon.spy()
96
+ globalThis.addEventListener(EventTypes.Authorization.OAuth2.removeToken, spy)
97
+ Events.Authorization.OAuth2.removeToken(config)
98
+ globalThis.removeEventListener(EventTypes.Authorization.OAuth2.removeToken, spy)
99
+ const e = spy.args[0][0]
100
+ const cnf = e.detail
101
+ delete cnf.result
102
+ assert.deepEqual(cnf, config)
103
+ })
104
+ })
105
+
106
+ test.group('Events > Authorization > Events.Authorization > Oidc > authorize()', () => {
107
+ const config = { responseType: 'implicit' }
108
+
109
+ test('dispatches the event', async ({ assert }) => {
110
+ const spy = sinon.spy()
111
+ globalThis.addEventListener(EventTypes.Authorization.Oidc.authorize, spy)
112
+ Events.Authorization.Oidc.authorize(config)
113
+ globalThis.removeEventListener(EventTypes.Authorization.Oidc.authorize, spy)
114
+ assert.isTrue(spy.calledOnce)
115
+ })
116
+
117
+ test('has the configuration on the detail', async ({ assert }) => {
118
+ const spy = sinon.spy()
119
+ globalThis.addEventListener(EventTypes.Authorization.Oidc.authorize, spy)
120
+ Events.Authorization.Oidc.authorize(config)
121
+ globalThis.removeEventListener(EventTypes.Authorization.Oidc.authorize, spy)
122
+ const e = spy.args[0][0]
123
+ const cnf = e.detail
124
+ delete cnf.result
125
+ assert.deepEqual(cnf, config)
126
+ })
127
+ })
128
+
129
+ test.group('Events > Authorization > Events.Authorization > Oidc > removeToken()', () => {
130
+ const config = { clientId: 'id', authorizationUri: 'test' }
131
+
132
+ test('dispatches the event', async ({ assert }) => {
133
+ const spy = sinon.spy()
134
+ globalThis.addEventListener(EventTypes.Authorization.Oidc.removeTokens, spy)
135
+ Events.Authorization.Oidc.removeTokens(config)
136
+ globalThis.removeEventListener(EventTypes.Authorization.Oidc.removeTokens, spy)
137
+ assert.isTrue(spy.calledOnce)
138
+ })
139
+
140
+ test('has the configuration on the detail', async ({ assert }) => {
141
+ const spy = sinon.spy()
142
+ globalThis.addEventListener(EventTypes.Authorization.Oidc.removeTokens, spy)
143
+ Events.Authorization.Oidc.removeTokens(config) // Calling removeToken
144
+ globalThis.removeEventListener(EventTypes.Authorization.Oidc.removeTokens, spy)
145
+ const e = spy.args[0][0]
146
+ const cnf = e.detail
147
+ delete cnf.result
148
+ assert.deepEqual(cnf, config)
149
+ })
150
+ })