@api-client/core 0.18.5 → 0.18.7
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/build/src/lib/dom_purify.d.ts +4 -0
- package/build/src/lib/dom_purify.d.ts.map +1 -0
- package/build/src/lib/dom_purify.js +23 -0
- package/build/src/lib/dom_purify.js.map +1 -0
- package/build/src/modeling/helpers/database.js +2 -2
- package/build/src/modeling/helpers/database.js.map +1 -1
- package/build/src/modeling/importers/CsvImporter.d.ts +40 -6
- package/build/src/modeling/importers/CsvImporter.d.ts.map +1 -1
- package/build/src/modeling/importers/CsvImporter.js +75 -2
- package/build/src/modeling/importers/CsvImporter.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/data/models/example-generator-api.json +25 -25
- package/package.json +4 -2
- package/src/lib/dom_purify.ts +25 -0
- package/src/modeling/helpers/database.ts +2 -2
- package/src/modeling/importers/CsvImporter.ts +115 -5
- package/tests/unit/lib/dom_purify.spec.ts +12 -0
- package/tests/unit/modeling/importers/csv_importer.spec.ts +351 -0
|
@@ -42062,22 +42062,22 @@
|
|
|
42062
42062
|
"@id": "#209"
|
|
42063
42063
|
},
|
|
42064
42064
|
{
|
|
42065
|
-
"@id": "#
|
|
42065
|
+
"@id": "#203"
|
|
42066
42066
|
},
|
|
42067
42067
|
{
|
|
42068
|
-
"@id": "#
|
|
42068
|
+
"@id": "#191"
|
|
42069
42069
|
},
|
|
42070
42070
|
{
|
|
42071
|
-
"@id": "#
|
|
42071
|
+
"@id": "#194"
|
|
42072
42072
|
},
|
|
42073
42073
|
{
|
|
42074
|
-
"@id": "#
|
|
42074
|
+
"@id": "#206"
|
|
42075
42075
|
},
|
|
42076
42076
|
{
|
|
42077
|
-
"@id": "#
|
|
42077
|
+
"@id": "#197"
|
|
42078
42078
|
},
|
|
42079
42079
|
{
|
|
42080
|
-
"@id": "#
|
|
42080
|
+
"@id": "#200"
|
|
42081
42081
|
},
|
|
42082
42082
|
{
|
|
42083
42083
|
"@id": "#209"
|
|
@@ -42810,13 +42810,13 @@
|
|
|
42810
42810
|
"@id": "#219"
|
|
42811
42811
|
},
|
|
42812
42812
|
{
|
|
42813
|
-
"@id": "#
|
|
42813
|
+
"@id": "#216"
|
|
42814
42814
|
},
|
|
42815
42815
|
{
|
|
42816
|
-
"@id": "#
|
|
42816
|
+
"@id": "#210"
|
|
42817
42817
|
},
|
|
42818
42818
|
{
|
|
42819
|
-
"@id": "#
|
|
42819
|
+
"@id": "#213"
|
|
42820
42820
|
},
|
|
42821
42821
|
{
|
|
42822
42822
|
"@id": "#219"
|
|
@@ -43436,7 +43436,7 @@
|
|
|
43436
43436
|
"doc:ExternalDomainElement",
|
|
43437
43437
|
"doc:DomainElement"
|
|
43438
43438
|
],
|
|
43439
|
-
"doc:raw": "
|
|
43439
|
+
"doc:raw": "addressType: 'REGISTERED-OFFICE-ADDRESS'\nstreetName: 'UITBREIDINGSTRAAT'\nhouseNumber: '84'\nhouseNumberAddition: '/1'\npostalCode: '2600'\ncity: 'BERCHEM (ANTWERPEN)'\ncountry: 'Belgium'\ncountryCode: 'BE'\nfullFormatedAddress: \"UITBREIDINGSTRAAT 84 /1, 2600 BERCHEM (ANTWERPEN), BELIUM\"\n",
|
|
43440
43440
|
"core:mediaType": "application/yaml",
|
|
43441
43441
|
"sourcemaps:sources": [
|
|
43442
43442
|
{
|
|
@@ -43457,7 +43457,7 @@
|
|
|
43457
43457
|
"doc:ExternalDomainElement",
|
|
43458
43458
|
"doc:DomainElement"
|
|
43459
43459
|
],
|
|
43460
|
-
"doc:raw": "
|
|
43460
|
+
"doc:raw": "code: '5'\ndescription: 'Limited company'\n",
|
|
43461
43461
|
"core:mediaType": "application/yaml",
|
|
43462
43462
|
"sourcemaps:sources": [
|
|
43463
43463
|
{
|
|
@@ -43478,7 +43478,7 @@
|
|
|
43478
43478
|
"doc:ExternalDomainElement",
|
|
43479
43479
|
"doc:DomainElement"
|
|
43480
43480
|
],
|
|
43481
|
-
"doc:raw": "code: '
|
|
43481
|
+
"doc:raw": "code: 'J'\ndescription: 'Information and communication'\n",
|
|
43482
43482
|
"core:mediaType": "application/yaml",
|
|
43483
43483
|
"sourcemaps:sources": [
|
|
43484
43484
|
{
|
|
@@ -43499,7 +43499,7 @@
|
|
|
43499
43499
|
"doc:ExternalDomainElement",
|
|
43500
43500
|
"doc:DomainElement"
|
|
43501
43501
|
],
|
|
43502
|
-
"doc:raw": "
|
|
43502
|
+
"doc:raw": "code: '7487'\ndescription: 'Financial and insurance activities'\ntype: \"PRIMARY\"\nclassificationCode: 'BE_NACEBEL2008'\nactivityGroupCode: 'ABCDE'\n",
|
|
43503
43503
|
"core:mediaType": "application/yaml",
|
|
43504
43504
|
"sourcemaps:sources": [
|
|
43505
43505
|
{
|
|
@@ -43520,7 +43520,7 @@
|
|
|
43520
43520
|
"doc:ExternalDomainElement",
|
|
43521
43521
|
"doc:DomainElement"
|
|
43522
43522
|
],
|
|
43523
|
-
"doc:raw": "
|
|
43523
|
+
"doc:raw": "countryCode: \"BE\"\ngraydonEnterpriseId: 1057155523\nregistrationId: \"0422319093\"\nvatNumber: \"BE0422319093\"\ngraydonCompanyId: \"0422319093\"\nisBranchOffice: false\n",
|
|
43524
43524
|
"core:mediaType": "application/yaml",
|
|
43525
43525
|
"sourcemaps:sources": [
|
|
43526
43526
|
{
|
|
@@ -43541,7 +43541,7 @@
|
|
|
43541
43541
|
"doc:ExternalDomainElement",
|
|
43542
43542
|
"doc:DomainElement"
|
|
43543
43543
|
],
|
|
43544
|
-
"doc:raw": "
|
|
43544
|
+
"doc:raw": "class: '3'\ndescription: '150 - 300'\nnumberOfFte: 5500\nnumberOfEmployees: 5232\n",
|
|
43545
43545
|
"core:mediaType": "application/yaml",
|
|
43546
43546
|
"sourcemaps:sources": [
|
|
43547
43547
|
{
|
|
@@ -44232,7 +44232,7 @@
|
|
|
44232
44232
|
"doc:ExternalDomainElement",
|
|
44233
44233
|
"doc:DomainElement"
|
|
44234
44234
|
],
|
|
44235
|
-
"doc:raw": "type: 'GENERAL'\ncountryDialCode : '+32'\nareaCode : '
|
|
44235
|
+
"doc:raw": "type: 'GENERAL'\ncountryDialCode : '+32'\nareaCode : '21'\nsubscriberNumber: '12.87.00'\nformatted: '+32-(0)21 302099'\n",
|
|
44236
44236
|
"core:mediaType": "application/yaml",
|
|
44237
44237
|
"sourcemaps:sources": [
|
|
44238
44238
|
{
|
|
@@ -44253,7 +44253,7 @@
|
|
|
44253
44253
|
"doc:ExternalDomainElement",
|
|
44254
44254
|
"doc:DomainElement"
|
|
44255
44255
|
],
|
|
44256
|
-
"doc:raw": "type: 'GENERAL'\
|
|
44256
|
+
"doc:raw": "-\n type: 'GENERAL'\n value: 'info@company.be'\n-\n type: 'IT_DEPT'\n value: 'it-service@company.be'\n",
|
|
44257
44257
|
"core:mediaType": "application/yaml",
|
|
44258
44258
|
"sourcemaps:sources": [
|
|
44259
44259
|
{
|
|
@@ -44274,7 +44274,7 @@
|
|
|
44274
44274
|
"doc:ExternalDomainElement",
|
|
44275
44275
|
"doc:DomainElement"
|
|
44276
44276
|
],
|
|
44277
|
-
"doc:raw": "
|
|
44277
|
+
"doc:raw": "type: 'GENERAL'\ncountryDialCode : '+32'\nareaCode : '22'\nsubscriberNumber: '12.87.00'\nformatted: '+32-(0)22 000000'\n",
|
|
44278
44278
|
"core:mediaType": "application/yaml",
|
|
44279
44279
|
"sourcemaps:sources": [
|
|
44280
44280
|
{
|
|
@@ -44756,12 +44756,12 @@
|
|
|
44756
44756
|
{
|
|
44757
44757
|
"@id": "#193/source-map/lexical/element_0",
|
|
44758
44758
|
"sourcemaps:element": "amf://id#193",
|
|
44759
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44759
|
+
"sourcemaps:value": "[(1,0)-(10,0)]"
|
|
44760
44760
|
},
|
|
44761
44761
|
{
|
|
44762
44762
|
"@id": "#196/source-map/lexical/element_0",
|
|
44763
44763
|
"sourcemaps:element": "amf://id#196",
|
|
44764
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44764
|
+
"sourcemaps:value": "[(1,0)-(3,0)]"
|
|
44765
44765
|
},
|
|
44766
44766
|
{
|
|
44767
44767
|
"@id": "#199/source-map/lexical/element_0",
|
|
@@ -44771,17 +44771,17 @@
|
|
|
44771
44771
|
{
|
|
44772
44772
|
"@id": "#202/source-map/lexical/element_0",
|
|
44773
44773
|
"sourcemaps:element": "amf://id#202",
|
|
44774
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44774
|
+
"sourcemaps:value": "[(1,0)-(6,0)]"
|
|
44775
44775
|
},
|
|
44776
44776
|
{
|
|
44777
44777
|
"@id": "#205/source-map/lexical/element_0",
|
|
44778
44778
|
"sourcemaps:element": "amf://id#205",
|
|
44779
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44779
|
+
"sourcemaps:value": "[(1,0)-(7,0)]"
|
|
44780
44780
|
},
|
|
44781
44781
|
{
|
|
44782
44782
|
"@id": "#208/source-map/lexical/element_0",
|
|
44783
44783
|
"sourcemaps:element": "amf://id#208",
|
|
44784
|
-
"sourcemaps:value": "[(1,0)-(
|
|
44784
|
+
"sourcemaps:value": "[(1,0)-(5,0)]"
|
|
44785
44785
|
},
|
|
44786
44786
|
{
|
|
44787
44787
|
"@id": "#223/source-map/lexical/element_0",
|
|
@@ -45121,12 +45121,12 @@
|
|
|
45121
45121
|
{
|
|
45122
45122
|
"@id": "#215/source-map/lexical/element_0",
|
|
45123
45123
|
"sourcemaps:element": "amf://id#215",
|
|
45124
|
-
"sourcemaps:value": "[(1,0)-(
|
|
45124
|
+
"sourcemaps:value": "[(1,0)-(7,0)]"
|
|
45125
45125
|
},
|
|
45126
45126
|
{
|
|
45127
45127
|
"@id": "#218/source-map/lexical/element_0",
|
|
45128
45128
|
"sourcemaps:element": "amf://id#218",
|
|
45129
|
-
"sourcemaps:value": "[(1,0)-(
|
|
45129
|
+
"sourcemaps:value": "[(1,0)-(6,0)]"
|
|
45130
45130
|
},
|
|
45131
45131
|
{
|
|
45132
45132
|
"@id": "#221/source-map/lexical/element_0",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@api-client/core",
|
|
3
3
|
"description": "The API Client's core client library. Works in NodeJS and in a ES enabled browser.",
|
|
4
|
-
"version": "0.18.
|
|
4
|
+
"version": "0.18.7",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./browser.js": {
|
|
@@ -94,7 +94,8 @@
|
|
|
94
94
|
"amf-json-ld-lib": "^0.0.15",
|
|
95
95
|
"chalk": "^5.4.1",
|
|
96
96
|
"console-table-printer": "^2.11.2",
|
|
97
|
-
"
|
|
97
|
+
"dompurify": "^3.2.6",
|
|
98
|
+
"jsdom": "^26.1.0",
|
|
98
99
|
"nanoid": "^5.1.5",
|
|
99
100
|
"ws": "^8.12.0",
|
|
100
101
|
"xpath": "^0.0.34"
|
|
@@ -154,6 +155,7 @@
|
|
|
154
155
|
"prepare": "husky && npm run fixes && npm run build:ts && npm run build:api-models",
|
|
155
156
|
"fixes": "node scripts/fix-rollup-plugin.js",
|
|
156
157
|
"tsc": "wireit",
|
|
158
|
+
"tsc:tests": "wireit",
|
|
157
159
|
"tsc:watch": "wireit",
|
|
158
160
|
"test:browser": "node --import ts-node-maintained/register/esm --enable-source-maps bin/test-web.ts --playwright --browsers chromium",
|
|
159
161
|
"test:browser:watch": "node --import ts-node-maintained/register/esm --enable-source-maps bin/test-web.ts --watch --playwright --browsers chromium",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import DOMPurify from 'dompurify'
|
|
2
|
+
|
|
3
|
+
let instance: typeof DOMPurify
|
|
4
|
+
|
|
5
|
+
function isNodeJsEnvironment(): boolean {
|
|
6
|
+
return (
|
|
7
|
+
// eslint-disable-next-line no-restricted-globals
|
|
8
|
+
typeof process !== 'undefined' &&
|
|
9
|
+
// eslint-disable-next-line no-restricted-globals
|
|
10
|
+
typeof process.versions !== 'undefined' &&
|
|
11
|
+
// eslint-disable-next-line no-restricted-globals
|
|
12
|
+
typeof process.versions.node !== 'undefined'
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
if (isNodeJsEnvironment()) {
|
|
16
|
+
const { JSDOM } = await import('jsdom')
|
|
17
|
+
const { window } = new JSDOM('<!DOCTYPE html>')
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
instance = DOMPurify(window)
|
|
21
|
+
} else {
|
|
22
|
+
instance = DOMPurify
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default instance
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import DOMPurify from 'isomorphic-dompurify'
|
|
2
1
|
import { snakeCase } from '@pawel-up/jexl/string.js'
|
|
2
|
+
import DOMPurify from '../../lib/dom_purify.js'
|
|
3
3
|
|
|
4
4
|
export function sanitizeInput(input: string): string {
|
|
5
|
-
return DOMPurify.
|
|
5
|
+
return DOMPurify.sanitize(input)
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -1,11 +1,43 @@
|
|
|
1
|
-
import { type CSVOptions, CSVParser, type ParseResult } from '@pawel-up/csv'
|
|
1
|
+
import { type CSVOptions, CSVParser, type ParseResult as ParseResult_ } from '@pawel-up/csv'
|
|
2
2
|
import type { DataDomain } from '../DataDomain.js'
|
|
3
3
|
import { sanitizeInput, toDatabaseColumnName, toDatabaseTableName } from '../helpers/database.js'
|
|
4
4
|
import type { DomainPropertySchema } from '../DomainProperty.js'
|
|
5
5
|
import type { DomainEntity } from '../DomainEntity.js'
|
|
6
6
|
import type { DomainModel } from '../DomainModel.js'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
interface ParseResult extends ParseResult_ {
|
|
9
|
+
/**
|
|
10
|
+
* The file name of the CSV that was parsed.
|
|
11
|
+
* If the CSV is provided as a string, it will be named `inline_csv_0`, `inline_csv_1`, etc.
|
|
12
|
+
*/
|
|
13
|
+
file: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let increment = 0
|
|
17
|
+
|
|
18
|
+
interface ImportResult {
|
|
19
|
+
/**
|
|
20
|
+
* The DomainEntity created from the CSV data.
|
|
21
|
+
*/
|
|
22
|
+
entity: DomainEntity
|
|
23
|
+
/**
|
|
24
|
+
* The DomainModel that contains the imported entity.
|
|
25
|
+
*/
|
|
26
|
+
model: DomainModel
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ImportManyResult {
|
|
30
|
+
/**
|
|
31
|
+
* An array of DomainEntity instances created from the CSV data.
|
|
32
|
+
*/
|
|
33
|
+
entities: DomainEntity[]
|
|
34
|
+
/**
|
|
35
|
+
* The DomainModel that contains all imported entities.
|
|
36
|
+
*/
|
|
37
|
+
model: DomainModel
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type { CSVOptions, ParseResult, ImportResult, ImportManyResult }
|
|
9
41
|
|
|
10
42
|
/**
|
|
11
43
|
* Imports CSV data into a DataDomain.
|
|
@@ -33,8 +65,19 @@ export class CsvImporter {
|
|
|
33
65
|
* @param csv The CSV content to parse, as a File object or a string.
|
|
34
66
|
* @returns A promise that resolves with the parsed result.
|
|
35
67
|
*/
|
|
36
|
-
public parse(csv: File | string): Promise<ParseResult> {
|
|
37
|
-
|
|
68
|
+
public async parse(csv: File | string): Promise<ParseResult> {
|
|
69
|
+
const result = await this.parser.parse(csv)
|
|
70
|
+
return {
|
|
71
|
+
...result,
|
|
72
|
+
file: typeof csv === 'string' ? 'inline_csv_' + increment++ : csv.name,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private removeExtension(name: string): string {
|
|
77
|
+
if (name.endsWith('.csv')) {
|
|
78
|
+
name = name.slice(0, -4) // Remove .csv extension if present
|
|
79
|
+
}
|
|
80
|
+
return name
|
|
38
81
|
}
|
|
39
82
|
|
|
40
83
|
/**
|
|
@@ -45,7 +88,8 @@ export class CsvImporter {
|
|
|
45
88
|
* @param modelName The name to be used for the created `DomainModel` and `DomainEntity`.
|
|
46
89
|
* @returns A promise that resolves when the import is complete.
|
|
47
90
|
*/
|
|
48
|
-
public async import(data: ParseResult, modelName: string): Promise<
|
|
91
|
+
public async import(data: ParseResult, modelName: string): Promise<ImportResult> {
|
|
92
|
+
modelName = this.removeExtension(modelName)
|
|
49
93
|
const name = toDatabaseTableName(modelName, 'imported_cvs_data')
|
|
50
94
|
const model = this.domain.addModel({ info: { name } })
|
|
51
95
|
const entity = model.addEntity({ info: { name } })
|
|
@@ -88,4 +132,70 @@ export class CsvImporter {
|
|
|
88
132
|
}
|
|
89
133
|
return { entity, model }
|
|
90
134
|
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Imports multiple parsed CSV data structures into the same data model.
|
|
138
|
+
* Creates a single `DomainModel` with multiple `DomainEntity` instances, one for each CSV data structure.
|
|
139
|
+
* Each entity represents the structure of its corresponding CSV file.
|
|
140
|
+
* @param data Array of objects containing parsed CSV data and entity names.
|
|
141
|
+
* @param modelName The name to be used for the created `DomainModel`.
|
|
142
|
+
* @returns A promise that resolves with the created model and all entities.
|
|
143
|
+
*/
|
|
144
|
+
public async importMany(data: ParseResult[], modelName: string): Promise<ImportManyResult> {
|
|
145
|
+
modelName = this.removeExtension(modelName)
|
|
146
|
+
const name = toDatabaseTableName(modelName, 'imported_cvs_data')
|
|
147
|
+
const model = this.domain.addModel({ info: { name } })
|
|
148
|
+
const sanitizedModelName = sanitizeInput(modelName)
|
|
149
|
+
if (name !== sanitizedModelName) {
|
|
150
|
+
model.info.displayName = sanitizedModelName
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const entities: DomainEntity[] = []
|
|
154
|
+
|
|
155
|
+
for (const { format, values, file } of data) {
|
|
156
|
+
const cleanFile = this.removeExtension(file)
|
|
157
|
+
const entityDbName = toDatabaseTableName(cleanFile, `entity_${entities.length}`)
|
|
158
|
+
const entity = model.addEntity({ info: { name: entityDbName } })
|
|
159
|
+
const sanitizedEntityName = sanitizeInput(cleanFile)
|
|
160
|
+
if (entityDbName !== sanitizedEntityName) {
|
|
161
|
+
entity.info.displayName = sanitizedEntityName
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const exampleRow = values[0]
|
|
165
|
+
for (const row of format) {
|
|
166
|
+
const { index, name, type, format } = row
|
|
167
|
+
const columnName = toDatabaseColumnName(name, `column_${index}`)
|
|
168
|
+
const schema: Partial<DomainPropertySchema> = {
|
|
169
|
+
info: { name: columnName },
|
|
170
|
+
type,
|
|
171
|
+
}
|
|
172
|
+
if (format) {
|
|
173
|
+
schema.bindings = [
|
|
174
|
+
{
|
|
175
|
+
type: 'web',
|
|
176
|
+
schema: {
|
|
177
|
+
format: format === 'integer' ? 'int64' : 'double',
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
if (Array.isArray(exampleRow) && exampleRow[index]) {
|
|
183
|
+
const value = exampleRow[index]
|
|
184
|
+
if (value !== undefined && value !== null) {
|
|
185
|
+
schema.schema = {
|
|
186
|
+
examples: [sanitizeInput(String(value))],
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const prop = entity.addProperty(schema)
|
|
191
|
+
const sn = sanitizeInput(name)
|
|
192
|
+
if (sn !== columnName) {
|
|
193
|
+
prop.info.displayName = sn
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
entities.push(entity)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return { entities, model }
|
|
200
|
+
}
|
|
91
201
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { test } from '@japa/runner'
|
|
2
|
+
import DOMPurify from '../../../src/lib/dom_purify.js'
|
|
3
|
+
|
|
4
|
+
test('DOMPurify is initialized correctly', ({ assert }) => {
|
|
5
|
+
assert.isFunction(DOMPurify.sanitize, 'DOMPurify should have a sanitize function')
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
test('DOMPurify sanitizes input correctly', ({ assert }) => {
|
|
9
|
+
const unsafeInput = '<script>alert("XSS")</script>'
|
|
10
|
+
const safeOutput = DOMPurify.sanitize(unsafeInput)
|
|
11
|
+
assert.equal(safeOutput, '', 'Sanitized output should be empty for unsafe input')
|
|
12
|
+
})
|