@notabene/javascript-sdk 1.34.0 → 2.0.0-next.3

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 (73) hide show
  1. package/.gitlab-ci.yml +12 -1
  2. package/.husky/commit-msg +0 -0
  3. package/.husky/pre-commit +0 -0
  4. package/.husky/pre-push +1 -1
  5. package/.vscode/extensions.json +1 -5
  6. package/.vscode/settings.json +4 -4
  7. package/.yarn/releases/yarn-berry.cjs +925 -0
  8. package/.yarnrc.yml +3 -0
  9. package/README.md +126 -69
  10. package/api-extractor.json +434 -0
  11. package/dist/notabene.cjs +1 -0
  12. package/dist/notabene.d.ts +678 -0
  13. package/dist/notabene.js +187 -0
  14. package/dist/tsdoc-metadata.json +11 -0
  15. package/eslint.config.js +24 -0
  16. package/etc/javascript-sdk.api.md +363 -0
  17. package/index.html +135 -0
  18. package/package.json +29 -54
  19. package/src/__tests__/notabene.test.ts +131 -0
  20. package/src/arbitraries.ts +13 -0
  21. package/src/components/EmbeddedComponent.ts +110 -0
  22. package/src/components/__tests__/EmbeddedComponent.test.ts +160 -0
  23. package/src/ivms/types.ts +2 -0
  24. package/src/notabene.ts +174 -290
  25. package/src/types.ts +380 -49
  26. package/src/utils/MessageEventManager.ts +55 -0
  27. package/src/utils/__tests__/MessageEventManager.test.ts +95 -0
  28. package/src/utils/arbitraries.ts +21 -0
  29. package/src/utils/caip.ts +16 -0
  30. package/src/utils/urls.ts +15 -0
  31. package/temp/javascript-sdk.api.json +3509 -0
  32. package/temp/javascript-sdk.api.md +363 -0
  33. package/ts-out/src/__tests__/notabene.test.d.ts +1 -0
  34. package/ts-out/src/__tests__/notabene.test.js +97 -0
  35. package/ts-out/src/arbitraries.d.ts +4 -0
  36. package/ts-out/src/arbitraries.js +8 -0
  37. package/ts-out/src/components/EmbeddedComponent.d.ts +25 -0
  38. package/ts-out/src/components/EmbeddedComponent.js +87 -0
  39. package/ts-out/src/components/__tests__/EmbeddedComponent.test.d.ts +1 -0
  40. package/ts-out/src/components/__tests__/EmbeddedComponent.test.js +132 -0
  41. package/ts-out/src/ivms/types.d.ts +252 -0
  42. package/ts-out/src/ivms/types.js +1 -0
  43. package/ts-out/src/notabene.d.ts +35 -0
  44. package/ts-out/src/notabene.js +60 -0
  45. package/ts-out/src/types.d.ts +357 -0
  46. package/ts-out/src/types.js +85 -0
  47. package/ts-out/src/utils/MessageEventManager.d.ts +12 -0
  48. package/ts-out/src/utils/MessageEventManager.js +45 -0
  49. package/ts-out/src/utils/__tests__/MessageEventManager.test.d.ts +1 -0
  50. package/ts-out/src/utils/__tests__/MessageEventManager.test.js +73 -0
  51. package/ts-out/src/utils/arbitraries.d.ts +6 -0
  52. package/ts-out/src/utils/arbitraries.js +7 -0
  53. package/ts-out/src/utils/caip.d.ts +13 -0
  54. package/ts-out/src/utils/caip.js +15 -0
  55. package/ts-out/src/utils/urls.d.ts +1 -0
  56. package/ts-out/src/utils/urls.js +14 -0
  57. package/ts-out/tsconfig.tsbuildinfo +1 -0
  58. package/tsconfig.json +15 -11
  59. package/tsconfig.tsbuildinfo +1 -0
  60. package/vite.config.js +13 -0
  61. package/.envrc.template +0 -1
  62. package/.eslintignore +0 -3
  63. package/.eslintrc.js +0 -13
  64. package/.nvmrc +0 -1
  65. package/.tool-versions +0 -3
  66. package/dist/es/index.js +0 -1
  67. package/dist/lib/index.js +0 -376
  68. package/jest.config.js +0 -5
  69. package/public/index.html +0 -93
  70. package/public/js/.gitkeep +0 -0
  71. package/src/lib/index.ts +0 -2
  72. package/src/module/index.ts +0 -10
  73. package/src/zoidComponentProps.ts +0 -81
package/index.html ADDED
@@ -0,0 +1,135 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ ​
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <title>Notabene Widget Example</title>
9
+ <script type="module">
10
+ import Notabene from '/src/notabene.ts';
11
+ const nb = new Notabene({
12
+ nodeUrl: 'https://api-qa.eu.notabene.dev',
13
+ // uxUrl: 'https://connect.notabene.dev',
14
+ uxUrl: 'http://localhost:8000',
15
+ theme: {
16
+ // primaryColor: '#fff',
17
+ // primaryFontColor: '#fff',
18
+ // secondaryFontColor: '#fff',
19
+ // backgroundColor: 'black',
20
+ // mode: 'dark',
21
+ },
22
+ // transactionTypeAllowed: 'VASP_2_VASP_ONLY',
23
+ // nonCustodialDeclarationType: 'DECLARATION',
24
+ // optInFeatures: ['REUSE_ADDRESS_OWNERSHIP_PROOF'],
25
+
26
+ // @deprecated
27
+ // beneficiaryDetails: ['beneficiaryNationalIdentification'],
28
+
29
+ // fieldsProps: {
30
+ // nationalIdentification: {
31
+ // forceDisplay: true,
32
+ // },
33
+ // dateAndPlaceOfBirth: {
34
+ // forceDisplay: true,
35
+ // },
36
+ // geographicAddress: {
37
+ // forceDisplay: true,
38
+ // optional: true
39
+ // },
40
+ // counterparty: {
41
+ // optional: true
42
+ // }
43
+ // }
44
+ // fallbacks: [{flow: "WALLET_NOT_SUPPORTED", action: "REJECT"}]
45
+ });
46
+
47
+ const component = nb.createWithdrawalAssist({
48
+ asset: 'ETH',
49
+ customer: {
50
+ name: 'John Doe',
51
+ type: 'natural',
52
+ },
53
+ destination: '0x8e73ea19a72e89f20339f5a9e4cda8e4b230455c',
54
+ amountDecimal: 40.1,
55
+ });
56
+ component.mount('#withdrawal-assist');
57
+ component.on('error', console.error);
58
+ component.on('complete', (result) => {
59
+ console.log('result', result);
60
+ document.getElementById('result').innerText = JSON.stringify(
61
+ result,
62
+ null,
63
+ 2,
64
+ );
65
+ });
66
+
67
+ const fullscreen = nb.createWithdrawalAssist(
68
+ {
69
+ asset: 'BTC',
70
+ destination: '0x8e73ea19a72e89f20339f5a9e4cda8e4b230455c',
71
+ amountDecimal: 40.1,
72
+ },
73
+ {},
74
+ {
75
+ redirectUri: document.location.href,
76
+ },
77
+ );
78
+ document
79
+ .getElementById('open-fullscreen')
80
+ .addEventListener('click', () => {
81
+ document.location.hash = undefined;
82
+ fullscreen.open();
83
+ });
84
+ if (document.location.hash) {
85
+ const response = nb.decodeFragmentToObject(document.location.hash);
86
+ console.log(response);
87
+ document.getElementById('result').innerText = JSON.stringify(
88
+ JSON.parse(response['message']),
89
+ null,
90
+ 2,
91
+ );
92
+ }
93
+ </script>
94
+ <link
95
+ rel="stylesheet"
96
+ href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
97
+ />
98
+
99
+ <style>
100
+ body {
101
+ margin: 0;
102
+ padding: 1em;
103
+ max-width: 800px;
104
+ }
105
+ #container {
106
+ max-width: 100vw;
107
+ }
108
+ pre {
109
+ border: 1px solid #ccc;
110
+ padding: 1em;
111
+ font-family: monospace;
112
+ }
113
+ </style>
114
+ </head>
115
+ ​
116
+ <body>
117
+ <h1>Notabene Javascript SDK</h1>
118
+ <section>
119
+ <h2>Withdrawal Assist</h2>
120
+ <section>
121
+ <h3>Embedded</h3>
122
+ <div id="withdrawal-assist"></div>
123
+ </section>
124
+ <section>
125
+ <h3>Full Screen</h3>
126
+ <button id="open-fullscreen">Open fullscreen</button>
127
+ </section>
128
+ <section>
129
+ <h3>result</h3>
130
+ <pre id="result"></pre>
131
+ </section>
132
+ </section>
133
+ </body>
134
+ ​
135
+ </html>
package/package.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "@notabene/javascript-sdk",
3
+ "type": "module",
3
4
  "description": "JavaScript SDK for Notabene",
4
5
  "homepage": "https://gitlab.com/notabene/open-source/javascript-sdk",
5
6
  "repository": {
@@ -8,74 +9,52 @@
8
9
  },
9
10
  "author": "Notabene <developers@notabene.id>",
10
11
  "license": "MIT",
11
- "version": "1.34.0",
12
- "main": "dist/index.js",
13
- "packageManager": "yarn@3.2.2",
12
+ "version": "2.0.0-next.3",
13
+ "source": "src/notabene.ts",
14
+ "main": "dist/notabene.js",
15
+ "types": "dist/notabene.d.ts",
14
16
  "browserslist": "> 0.5%, last 2 versions, not dead",
15
- "targets": {
16
- "es": {
17
- "source": "src/module/index.ts",
18
- "isLibrary": false,
19
- "context": "browser",
20
- "outputFormat": "esmodule"
21
- },
22
- "lib": {
23
- "source": "src/lib/index.ts",
24
- "isLibrary": true,
25
- "context": "node",
26
- "outputFormat": "commonjs"
27
- }
28
- },
29
- "publishConfig": {
30
- "access": "public"
31
- },
32
- "alias": {
33
- "process": false,
34
- "buffer": false
35
- },
36
17
  "scripts": {
37
18
  "prepare": "husky install",
19
+ "dev": "vite",
20
+ "preview": "vite preview",
38
21
  "watch": "parcel watch",
39
- "dev": "sirv --dev --port 3002 public",
40
22
  "build:dev": "yarn build && yarn dev",
41
- "build": "yarn build:clean && yarn build:compile",
42
- "build:clean": "rimraf dist/ .parcel-cache/",
43
- "build:compile": "parcel build --no-source-maps && cp dist/es/index.js public/js/notabene.js",
23
+ "build:clean": "rimraf dist/ ts-out",
24
+ "build": "vite build && tsc && api-extractor run",
44
25
  "lint": "eslint .",
45
- "test": "jest --passWithNoTests --runInBand",
46
- "release": "semantic-release"
26
+ "test": "vitest",
27
+ "release": "semantic-release",
28
+ "publish": "yarn build && yarn semantic-release && npm publish --tag next"
47
29
  },
48
30
  "devDependencies": {
49
31
  "@commitlint/cli": "^16.2.1",
50
32
  "@commitlint/config-conventional": "^16.2.1",
51
- "@parcel/packager-ts": "^2.5.0",
52
- "@parcel/transformer-typescript-types": "^2.5.0",
33
+ "@eslint/js": "^9.7.0",
34
+ "@fast-check/vitest": "^0.1.2",
35
+ "@microsoft/api-extractor": "^7.47.7",
53
36
  "@semantic-release/git": "^10.0.1",
54
37
  "@semantic-release/gitlab": "^9.3.0",
55
38
  "@semantic-release/npm": "^9.0.1",
56
- "@types/jest": "^27.5.1",
57
- "@typescript-eslint/eslint-plugin": "^5.26.0",
58
- "@typescript-eslint/parser": "^5.26.0",
59
- "buffer": "^6.0.3",
60
- "eslint": "^8.16.0",
61
- "eslint-config-prettier": "^8.4.0",
62
- "eslint-config-standard": "^16.0.3",
39
+ "@typescript-eslint/eslint-plugin": "^8.2.0",
40
+ "@typescript-eslint/parser": "^8.2.0",
41
+ "eslint": "^9.9.0",
42
+ "eslint-config-prettier": "^9.1.0",
43
+ "eslint-config-standard": "^17.1.0",
63
44
  "eslint-plugin-import": "^2.25.4",
64
45
  "eslint-plugin-node": "^11.1.0",
65
- "eslint-plugin-prettier": "^4.0.0",
66
- "eslint-plugin-promise": "^6.0.0",
46
+ "eslint-plugin-prettier": "^5.2.1",
47
+ "fast-check": "^3.20.0",
48
+ "globals": "^15.8.0",
67
49
  "husky": "^7.0.4",
68
- "jest": "^28.1.0",
69
50
  "lint-staged": "^12.3.4",
70
51
  "organize-imports-cli": "^0.9.0",
71
- "parcel": "^2.5.0",
72
- "prettier": "^2.6.2",
73
- "process": "^0.11.10",
74
- "rimraf": "^3.0.2",
52
+ "prettier": "^3.3.3",
75
53
  "semantic-release": "^19.0.2",
76
- "sirv-cli": "^2.0.2",
77
- "ts-jest": "^28.0.3",
78
- "typescript": "^4.7.2"
54
+ "typescript": "^5.5.4",
55
+ "typescript-eslint": "^8.2.0",
56
+ "vite": "^5.4.2",
57
+ "vitest": "^2.0.5"
79
58
  },
80
59
  "lint-staged": {
81
60
  "*.ts": [
@@ -87,9 +66,5 @@
87
66
  "extends": [
88
67
  "@commitlint/config-conventional"
89
68
  ]
90
- },
91
- "dependencies": {
92
- "jsx-pragmatic": "^2.0.22",
93
- "zoid": "^9.0.86"
94
69
  }
95
- }
70
+ }
@@ -0,0 +1,131 @@
1
+ import { fc, test } from '@fast-check/vitest';
2
+ import { describe } from 'vitest';
3
+ import { abitraryCallbackOptions, arbitraryComponent } from '../arbitraries';
4
+ import Notabene from '../notabene';
5
+
6
+ describe('Notabene', () => {
7
+ describe('defaults', () => {
8
+ test.prop([
9
+ fc.string({ minLength: 3 }),
10
+ fc.webUrl(),
11
+ arbitraryComponent(),
12
+ fc.object(),
13
+ ])(
14
+ 'Should set the correct values',
15
+ (authToken, nodeUrl, component, value) => {
16
+ const notabene = new Notabene({
17
+ nodeUrl,
18
+ authToken,
19
+ });
20
+ const url = new URL(notabene.componentUrl(component, value));
21
+ return (
22
+ url.hash ===
23
+ `#authToken=${encodeURIComponent(authToken)}&value=${encodeURIComponent(JSON.stringify(value))}` &&
24
+ url.searchParams.get('nodeUrl') === nodeUrl &&
25
+ url.pathname === `/${component}`
26
+ );
27
+ },
28
+ );
29
+ });
30
+
31
+ describe('minimal', () => {
32
+ test.prop([arbitraryComponent(), fc.object()])(
33
+ 'Should set the correct values',
34
+ (component, value) => {
35
+ const notabene = new Notabene({});
36
+ const url = new URL(notabene.componentUrl(component, value));
37
+ return (
38
+ url.hash === `#value=${encodeURIComponent(JSON.stringify(value))}` &&
39
+ url.hostname === 'connect.notabene.id' &&
40
+ url.pathname === `/${component}`
41
+ );
42
+ },
43
+ );
44
+ });
45
+
46
+ describe('with uxUrl', () => {
47
+ test.prop([
48
+ fc.string({ minLength: 3 }),
49
+ fc.webUrl(),
50
+ arbitraryComponent(),
51
+ fc.webAuthority(),
52
+ fc.object(),
53
+ ])(
54
+ 'Should set the correct values',
55
+ (authToken, nodeUrl, component, uxAuthority, value) => {
56
+ const notabene = new Notabene({
57
+ nodeUrl,
58
+ authToken,
59
+ uxUrl: `https://${uxAuthority}`,
60
+ });
61
+ const url = new URL(notabene.componentUrl(component, value));
62
+ const [domain, port] = uxAuthority.split(':');
63
+ return (
64
+ url.hostname === domain &&
65
+ url.port === (port || '') &&
66
+ url.hash ===
67
+ `#authToken=${encodeURIComponent(authToken)}&value=${encodeURIComponent(JSON.stringify(value))}` &&
68
+ url.searchParams.get('nodeUrl') === nodeUrl &&
69
+ url.pathname === `/${component}`
70
+ );
71
+ },
72
+ );
73
+ });
74
+
75
+ describe('with value and configuration', () => {
76
+ test.prop([
77
+ fc.string({ minLength: 3 }),
78
+ fc.webUrl(),
79
+ arbitraryComponent(),
80
+ fc.object(),
81
+ fc.object(),
82
+ ])(
83
+ 'Should set the correct values',
84
+ (authToken, nodeUrl, component, value, configuration) => {
85
+ const notabene = new Notabene({
86
+ nodeUrl,
87
+ authToken,
88
+ });
89
+ const url = new URL(
90
+ notabene.componentUrl(component, value, configuration),
91
+ );
92
+ return (
93
+ url.hash ===
94
+ `#authToken=${encodeURIComponent(authToken)}&value=${encodeURIComponent(JSON.stringify(value))}&configuration=${encodeURIComponent(JSON.stringify(configuration))}` &&
95
+ url.searchParams.get('nodeUrl') === nodeUrl &&
96
+ url.pathname === `/${component}`
97
+ );
98
+ },
99
+ );
100
+ });
101
+
102
+ describe('with value and configuration', () => {
103
+ test.prop([
104
+ fc.string({ minLength: 3 }),
105
+ fc.webUrl(),
106
+ arbitraryComponent(),
107
+ fc.object(),
108
+ fc.object(),
109
+ abitraryCallbackOptions(),
110
+ ])(
111
+ 'Should set the correct values',
112
+ (authToken, nodeUrl, component, value, configuration, callbacks) => {
113
+ const notabene = new Notabene({
114
+ nodeUrl,
115
+ authToken,
116
+ });
117
+ const url = new URL(
118
+ notabene.componentUrl(component, value, configuration, callbacks),
119
+ );
120
+ return (
121
+ url.hash ===
122
+ `#authToken=${encodeURIComponent(authToken)}&value=${encodeURIComponent(JSON.stringify(value))}&configuration=${encodeURIComponent(JSON.stringify(configuration))}` &&
123
+ url.searchParams.get('nodeUrl') === nodeUrl &&
124
+ url.pathname === `/${component}` &&
125
+ url.searchParams.get('callback_url') === callbacks.callback &&
126
+ url.searchParams.get('redirect_uri') === callbacks.redirectUri
127
+ );
128
+ },
129
+ );
130
+ });
131
+ });
@@ -0,0 +1,13 @@
1
+ import fc from 'fast-check';
2
+ import { CallbackOptions } from './types';
3
+
4
+ const COMPONENT_MATCHER = /^[a-z0-9-_]+$/;
5
+ // Arbitraries
6
+ export const arbitraryComponent = (): fc.Arbitrary<string> =>
7
+ fc.stringMatching(COMPONENT_MATCHER);
8
+
9
+ export const abitraryCallbackOptions = (): fc.Arbitrary<CallbackOptions> =>
10
+ fc.record({
11
+ callback: fc.webUrl(),
12
+ redirectUri: fc.webUrl(),
13
+ });
@@ -0,0 +1,110 @@
1
+ import {
2
+ CMType,
3
+ HMType,
4
+ HostMessage,
5
+ Transaction,
6
+ TransactionResponse,
7
+ ValidationError,
8
+ } from '../types';
9
+ import {
10
+ MessageCallback,
11
+ MessageEventManager,
12
+ } from '../utils/MessageEventManager';
13
+
14
+ /**
15
+ * An embedded Notabene component
16
+ * @public
17
+ */
18
+ export default class EmbeddedComponent {
19
+ private _url: string;
20
+ private _value: Partial<Transaction>;
21
+ private _errors: ValidationError[] = [];
22
+
23
+ private iframe?: HTMLIFrameElement;
24
+ private eventManager: MessageEventManager;
25
+ private ready = false;
26
+
27
+ constructor(url: string, value: Partial<Transaction>) {
28
+ this._url = url;
29
+ this._value = value;
30
+ this.eventManager = new MessageEventManager();
31
+ this.on(CMType.INVALID, (message) => {
32
+ if (message.type === CMType.INVALID) {
33
+ this._errors = message.errors;
34
+ this._value = message.value as Transaction;
35
+ }
36
+ });
37
+ this.on(CMType.RESIZE, (message) => {
38
+ if (message.type === CMType.RESIZE) {
39
+ if (this.iframe) this.iframe.style.height = `${message.height}px`;
40
+ }
41
+ });
42
+ }
43
+
44
+ public get url(): string {
45
+ return this._url;
46
+ }
47
+
48
+ public get value(): Partial<Transaction> {
49
+ return this._value;
50
+ }
51
+
52
+ public get errors(): ValidationError[] {
53
+ return this._errors;
54
+ }
55
+
56
+ public open() {
57
+ document.location.href = this.url;
58
+ }
59
+
60
+ mount(parentId: string) {
61
+ this.iframe = document.createElement('iframe');
62
+ this.iframe.src = this.url;
63
+ this.iframe.style.width = '100%';
64
+ this.iframe.style.height = '100px';
65
+ this.iframe.style.border = 'none';
66
+ this.iframe.style.overflow = 'hidden';
67
+
68
+ window.addEventListener('message', (event) => {
69
+ if (event.source !== this.iframe?.contentWindow) {
70
+ return;
71
+ }
72
+ console.log('received message from iframe', event.data);
73
+ this.eventManager?.setPort(event.ports[0]);
74
+ this.ready = true;
75
+ });
76
+
77
+ const parent = document.querySelector(parentId);
78
+ if (!parent) throw new Error(`parentID ${parentId} not found`);
79
+ parent.appendChild(this.iframe);
80
+ this.iframe?.contentWindow?.focus();
81
+ }
82
+
83
+ send(message: HostMessage): void {
84
+ this.eventManager.send(message);
85
+ }
86
+
87
+ on(messageType: string, callback: MessageCallback): void {
88
+ this.eventManager.on(messageType, callback);
89
+ }
90
+
91
+ off(messageType: string, callback: MessageCallback): void {
92
+ this.eventManager.off(messageType, callback);
93
+ }
94
+
95
+ update(value: Partial<Transaction>) {
96
+ this._value = value;
97
+ this.send({ type: HMType.UPDATE, value });
98
+ }
99
+
100
+ completion(): Promise<TransactionResponse> {
101
+ return new Promise((resolve, reject) => {
102
+ this.on(CMType.COMPLETE, (message) => {
103
+ resolve((message as any).response);
104
+ });
105
+ this.on('error', (message) => {
106
+ reject(new Error((message as any).message));
107
+ });
108
+ });
109
+ }
110
+ }
@@ -0,0 +1,160 @@
1
+ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
2
+ import { CMType, HMType, HostMessage, Transaction } from '../../types';
3
+ import EmbeddedComponent from '../EmbeddedComponent';
4
+
5
+ const url = 'https://example.com';
6
+ const parentId = 'parent-container';
7
+ const value: Partial<Transaction> = { amountDecimal: 100 };
8
+
9
+ describe('EmbeddedComponent', () => {
10
+ describe('creation', () => {
11
+ let component: EmbeddedComponent;
12
+ beforeEach(() => {
13
+ component = new EmbeddedComponent(url, { amountDecimal: 100 });
14
+ });
15
+
16
+ test('sets value', () => {
17
+ expect(component.value).toStrictEqual(value);
18
+ });
19
+
20
+ test('sets url', () => {
21
+ expect(component.url).toBe(url);
22
+ });
23
+ });
24
+
25
+ describe('iframe', () => {
26
+ let document: Document;
27
+ let parentElement: HTMLElement;
28
+ let iframe: HTMLIFrameElement;
29
+ let window: Window;
30
+ let iframeWindow: Window;
31
+
32
+ beforeEach(() => {
33
+ parentElement = {
34
+ appendChild: vi.fn(),
35
+ } as unknown as HTMLElement;
36
+
37
+ iframe = {
38
+ style: {},
39
+ src: '',
40
+ contentWindow: iframeWindow,
41
+ setAttribute: vi.fn(),
42
+ addEventListener: vi.fn(),
43
+ } as unknown as HTMLIFrameElement;
44
+
45
+ document = {
46
+ createElement: vi.fn().mockReturnValue(iframe),
47
+ querySelector: vi.fn().mockReturnValue(parentElement),
48
+ } as unknown as Document;
49
+
50
+ window = {
51
+ addEventListener: vi.fn(),
52
+ } as unknown as Window;
53
+
54
+ vi.stubGlobal('document', document);
55
+ vi.stubGlobal('window', window);
56
+ });
57
+
58
+ afterEach(() => {
59
+ vi.unstubAllGlobals();
60
+ });
61
+
62
+ test('mount', () => {
63
+ const component = new EmbeddedComponent(url, value);
64
+ component.mount(parentId);
65
+ expect(iframe?.src).toBe(url);
66
+ expect(parentElement.appendChild).toHaveBeenCalledWith(iframe);
67
+ });
68
+
69
+ test('on', () => {
70
+ const component = new EmbeddedComponent(url, value);
71
+ const messageType = 'customEvent';
72
+ const callback = vi.fn();
73
+ component.on(messageType, callback);
74
+ component.mount(parentId);
75
+
76
+ // Simulate receiving a message of the specified type
77
+ // and assert that the callback is called with the correct payload.
78
+ });
79
+
80
+ test('off', () => {
81
+ const component = new EmbeddedComponent(url, value);
82
+ const messageType = 'customEvent';
83
+ const callback = vi.fn();
84
+ component.on(messageType, callback);
85
+
86
+ // Simulate receiving a message of the specified type
87
+ // and assert that the callback is called with the correct payload.
88
+
89
+ component.off(messageType, callback);
90
+
91
+ // Simulate receiving another message of the specified type
92
+ // and assert that the callback is not called.
93
+ });
94
+
95
+ describe('connected to iframe', () => {
96
+ let messageHandler: (event: MessageEvent) => void;
97
+ let port: MessagePort;
98
+ let handshake: MessageEvent;
99
+ let component: EmbeddedComponent;
100
+
101
+ beforeEach(() => {
102
+ port = {
103
+ postMessage: vi.fn(),
104
+ start: vi.fn(),
105
+ onmessage: null,
106
+ } as unknown as MessagePort;
107
+ handshake = {
108
+ source: iframe.contentWindow,
109
+ ports: [port],
110
+ } as unknown as MessageEvent;
111
+ window.addEventListener = vi
112
+ .fn()
113
+ .mockImplementation((event, handler) => {
114
+ messageHandler = handler;
115
+ });
116
+ component = new EmbeddedComponent(url, value);
117
+ component.mount(parentId);
118
+
119
+ expect(messageHandler).toBeDefined();
120
+ messageHandler(handshake);
121
+ });
122
+
123
+ test('send', () => {
124
+ const message: HostMessage = {
125
+ type: HMType.UPDATE,
126
+ value: { amountDecimal: 100 },
127
+ };
128
+ component.send(message);
129
+
130
+ // Assert that the message is sent to the embedded component
131
+ // and handled correctly.
132
+ });
133
+
134
+ test('update', () => {
135
+ const newValue: Partial<Transaction> = { amountDecimal: 200 };
136
+ component.update(newValue);
137
+
138
+ expect(component.value).toBe(newValue);
139
+ });
140
+
141
+ // TODO not sure this actually does what I want it to do
142
+ test('completion', async () => {
143
+ const response = { valid: true, value: 'completed' };
144
+ expect(port.onmessage).toBeDefined();
145
+
146
+ const promise = component.completion();
147
+ // @ts-expect-error - we've already verified that port exists
148
+ port.onmessage({
149
+ data: { type: CMType.COMPLETE, response },
150
+ } as MessageEvent);
151
+
152
+ promise.then((result) => {
153
+ expect(result).toBe(response);
154
+ });
155
+ return promise;
156
+ // Assert that the completion promise resolves with the expected response.
157
+ });
158
+ });
159
+ });
160
+ });
package/src/ivms/types.ts CHANGED
@@ -291,6 +291,8 @@ export type PayloadMetadata = {
291
291
 
292
292
  /**
293
293
  * IVMS101 definition
294
+ *
295
+ * @public
294
296
  */
295
297
  export type IVMS101 = {
296
298
  originator?: Originator;