clarity-js 0.8.45 → 0.8.47-beta

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.
@@ -16,7 +16,7 @@ export let data: Metadata = null;
16
16
  export let callbacks: MetadataCallbackOptions[] = [];
17
17
  export let electron = BooleanFlag.False;
18
18
  let consentStatus: ConsentState = null;
19
- let defaultStatus: ConsentState = { source: ConsentSource.API, ad_Storage: Constant.Denied, analytics_Storage: Constant.Denied };
19
+ let defaultStatus: ConsentState = { source: ConsentSource.Default, ad_Storage: Constant.Denied, analytics_Storage: Constant.Denied };
20
20
 
21
21
  export function start(): void {
22
22
  const ua = navigator && "userAgent" in navigator ? navigator.userAgent : Constant.Empty;
@@ -140,15 +140,15 @@ export function id(): string {
140
140
  //TODO: Remove this function once consentv2 is fully released
141
141
  export function consent(status = true): void {
142
142
  if (!status) {
143
- consentv2();
143
+ consentv2({ source: ConsentSource.APIv1, ad_Storage: Constant.Denied, analytics_Storage: Constant.Denied });
144
144
  return;
145
145
  }
146
146
 
147
- consentv2({ ad_Storage: Constant.Granted, analytics_Storage: Constant.Granted });
147
+ consentv2({ source: ConsentSource.APIv1, ad_Storage: Constant.Granted, analytics_Storage: Constant.Granted });
148
148
  trackConsent.consent();
149
149
  }
150
150
 
151
- export function consentv2(consentState: ConsentState = defaultStatus, source: number = ConsentSource.API): void {
151
+ export function consentv2(consentState: ConsentState = defaultStatus, source: number = ConsentSource.APIv2): void {
152
152
  const updatedStatus = {
153
153
  source: consentState.source ?? source,
154
154
  ad_Storage: normalizeConsent(consentState.ad_Storage, consentStatus?.ad_Storage),
@@ -160,6 +160,9 @@ export function consentv2(consentState: ConsentState = defaultStatus, source: nu
160
160
  updatedStatus.ad_Storage === consentStatus.ad_Storage &&
161
161
  updatedStatus.analytics_Storage === consentStatus.analytics_Storage
162
162
  ) {
163
+ consentStatus.source = updatedStatus.source;
164
+ trackConsent.trackConsentv2(getConsentData(consentStatus));
165
+ trackConsent.consent();
163
166
  return;
164
167
  }
165
168
 
@@ -11,6 +11,7 @@ import * as timeline from "@src/interaction/timeline";
11
11
  import * as unload from "@src/interaction/unload";
12
12
  import * as visibility from "@src/interaction/visibility";
13
13
  import * as focus from "@src/interaction/focus";
14
+ import * as pageshow from "@src/interaction/pageshow";
14
15
 
15
16
  export function start(): void {
16
17
  timeline.start();
@@ -21,6 +22,7 @@ export function start(): void {
21
22
  resize.start();
22
23
  visibility.start();
23
24
  focus.start();
25
+ pageshow.start();
24
26
  scroll.start();
25
27
  selection.start();
26
28
  change.start();
@@ -37,6 +39,7 @@ export function stop(): void {
37
39
  resize.stop();
38
40
  visibility.stop();
39
41
  focus.stop();
42
+ pageshow.stop();
40
43
  scroll.stop();
41
44
  selection.stop();
42
45
  change.stop();
@@ -0,0 +1,35 @@
1
+ import { Constant } from "@clarity-types/core";
2
+ import { Code, Severity } from "@clarity-types/data";
3
+ import * as clarity from "@src/clarity";
4
+ import api from "@src/core/api";
5
+ import measure from "@src/core/measure";
6
+ import * as internal from "@src/diagnostic/internal";
7
+
8
+ let bound = false;
9
+
10
+ export function start(): void {
11
+ // Only bind once - this listener must persist even when Clarity stops
12
+ // to detect when the page is restored from bfcache
13
+ if (!bound) {
14
+ try {
15
+ window[api(Constant.AddEventListener)]("pageshow", measure(handler) as EventListener, { capture: false, passive: true });
16
+ bound = true;
17
+ } catch {
18
+ /* do nothing */
19
+ }
20
+ }
21
+ }
22
+
23
+ function handler(evt: PageTransitionEvent): void {
24
+ // The persisted property indicates if the page was loaded from bfcache
25
+ if (evt && evt.persisted) {
26
+ // Restart Clarity since it was stopped when the page entered bfcache
27
+ clarity.start();
28
+ internal.log(Code.BFCache, Severity.Info);
29
+ }
30
+ }
31
+
32
+ export function stop(): void {
33
+ // Intentionally don't remove the listener or reset 'bound' flag
34
+ // We need the listener to persist to detect bfcache restoration
35
+ }
@@ -0,0 +1,68 @@
1
+ import { expect, test } from "@playwright/test";
2
+ import hash from "@src/core/hash";
3
+
4
+ test.describe("Core Utilities - Hash", () => {
5
+ test("hash function should generate consistent hash for same input", () => {
6
+ const input = "test-string";
7
+ const hash1 = hash(input);
8
+ const hash2 = hash(input);
9
+
10
+ expect(hash1).toBe(hash2);
11
+ expect(hash1).toBeTruthy();
12
+ expect(typeof hash1).toBe("string");
13
+ });
14
+
15
+ test("hash function should generate different hashes for different inputs", () => {
16
+ const hash1 = hash("input1");
17
+ const hash2 = hash("input2");
18
+ const hash3 = hash("completely-different-input");
19
+
20
+ expect(hash1).not.toBe(hash2);
21
+ expect(hash2).not.toBe(hash3);
22
+ expect(hash1).not.toBe(hash3);
23
+ });
24
+
25
+ test("hash function should respect precision parameter", () => {
26
+ const input = "test-precision";
27
+ const hashNoPrecision = hash(input);
28
+ const hash16 = hash(input, 16);
29
+ const hash8 = hash(input, 8);
30
+
31
+ expect(hashNoPrecision).toBeTruthy();
32
+ expect(hash16).toBeTruthy();
33
+ expect(hash8).toBeTruthy();
34
+
35
+ // Hash with precision should be different from no precision
36
+ expect(hashNoPrecision).not.toBe(hash16);
37
+
38
+ // Verify precision limits: 2^16 = 65536, 2^8 = 256
39
+ const hash16Num = parseInt(hash16, 36);
40
+ const hash8Num = parseInt(hash8, 36);
41
+ expect(hash16Num).toBeLessThan(65536);
42
+ expect(hash8Num).toBeLessThan(256);
43
+ });
44
+
45
+ test("hash function should handle empty strings", () => {
46
+ const result = hash("");
47
+ expect(result).toBeTruthy();
48
+ expect(typeof result).toBe("string");
49
+ });
50
+
51
+ test("hash function should handle special characters", () => {
52
+ const hash1 = hash("test@example.com");
53
+ const hash2 = hash("test#$%^&*()");
54
+ const hash3 = hash("🎉🎊✨");
55
+
56
+ expect(hash1).toBeTruthy();
57
+ expect(hash2).toBeTruthy();
58
+ expect(hash3).toBeTruthy();
59
+ expect(hash1).not.toBe(hash2);
60
+ expect(hash2).not.toBe(hash3);
61
+ });
62
+
63
+ test("hash function should produce base36 output", () => {
64
+ const result = hash("test-base36");
65
+ // Base36 should only contain 0-9 and a-z
66
+ expect(/^[0-9a-z]+$/.test(result)).toBe(true);
67
+ });
68
+ });
@@ -0,0 +1,42 @@
1
+ import { expect, test } from "@playwright/test";
2
+ import { start, stop, time } from "@src/core/time";
3
+
4
+ test.describe("Time Utilities", () => {
5
+ test("time module should start and stop", () => {
6
+ start();
7
+ const time1 = time();
8
+
9
+ expect(time1).toBeGreaterThanOrEqual(0);
10
+
11
+ stop();
12
+ const time2 = time();
13
+
14
+ // After stop, time should still work but use different baseline
15
+ expect(time2).toBeGreaterThanOrEqual(0);
16
+ });
17
+
18
+ test("time function should track elapsed time", async () => {
19
+ start();
20
+ const time1 = time();
21
+
22
+ // Wait a bit
23
+ await new Promise((resolve) => setTimeout(resolve, 50));
24
+
25
+ const time2 = time();
26
+
27
+ expect(time2).toBeGreaterThan(time1);
28
+ expect(time2 - time1).toBeGreaterThanOrEqual(50);
29
+
30
+ stop();
31
+ });
32
+
33
+ test("time function should handle null event parameter", () => {
34
+ start();
35
+ const result = time(null);
36
+
37
+ expect(result).toBeGreaterThanOrEqual(0);
38
+ expect(typeof result).toBe("number");
39
+
40
+ stop();
41
+ });
42
+ });
package/tsconfig.json CHANGED
@@ -1,21 +1,12 @@
1
1
  {
2
+ "extends": "../../tsconfig.json",
2
3
  "compilerOptions": {
3
- "module": "esnext",
4
- "target": "es5",
5
- "lib": ["es6", "dom", "es2016", "es2017"],
6
- "moduleResolution": "node",
7
- "forceConsistentCasingInFileNames": true,
8
- "noImplicitReturns": true,
9
- "noUnusedLocals": true,
10
- "noUnusedParameters": true,
11
- "resolveJsonModule": true,
12
- "esModuleInterop": true,
13
4
  "baseUrl": ".",
14
5
  "paths": {
15
6
  "@src/*": ["src/*"],
16
7
  "@clarity-types/*": ["types/*"]
17
8
  }
18
9
  },
19
- "include":["src/**/*.ts","types/**/*.d.ts", "rollup.config.ts"],
20
- "exclude": ["test", "node_modules", "build"]
10
+ "include":["src/**/*.ts", "types/**/*.d.ts", "test/**/*.ts", "rollup.config.ts"],
11
+ "exclude": ["node_modules", "build"]
21
12
  }
package/types/data.d.ts CHANGED
@@ -212,6 +212,7 @@ export const enum Code {
212
212
  Config = 8,
213
213
  FunctionExecutionTime = 9,
214
214
  LeanLimit = 10,
215
+ BFCache = 11,
215
216
  }
216
217
 
217
218
  export const enum Severity {
package/types/index.d.ts CHANGED
@@ -11,12 +11,12 @@ interface Clarity {
11
11
  pause: () => void;
12
12
  resume: () => void;
13
13
  upgrade: (key: string) => void;
14
- consent: () => void;
15
- consentv2: () => void;
14
+ consent: (status?: boolean) => void;
15
+ consentv2: (consentState?: Data.ConsentState, source?: number) => void;
16
16
  event: (name: string, value: string) => void;
17
17
  set: (variable: string, value: string | string[]) => void;
18
18
  identify: (userId: string, sessionId?: string, pageId?: string, userHint?: string) => void;
19
- metadata: (callback: Data.MetadataCallback, wait?: boolean) => void;
19
+ metadata: (callback: Data.MetadataCallback, wait?: boolean, recall?: boolean, consentInfo?: boolean) => void;
20
20
  signal: (callback: Data.SignalCallback) => void;
21
21
  }
22
22
 
package/test/core.test.ts DELETED
@@ -1,139 +0,0 @@
1
- import { assert } from 'chai';
2
- import { Browser, Page } from 'playwright';
3
- import { changes, clicks, inputs, launch, markup, node, text } from './helper';
4
- import { decode } from "clarity-decode";
5
-
6
- let browser: Browser;
7
- let page: Page;
8
-
9
- describe('Core Tests', () => {
10
- before(async () => {
11
- browser = await launch();
12
- });
13
-
14
- beforeEach(async () => {
15
- page = await browser.newPage();
16
- await page.goto('about:blank');
17
- });
18
-
19
- afterEach(async () => {
20
- await page.close();
21
- });
22
-
23
- after(async () => {
24
- await browser.close();
25
- browser = null;
26
- });
27
-
28
- it('should mask sensitive content by default', async () => {
29
- let encoded: string[] = await markup(page, "core.html");
30
- let decoded = encoded.map(x => decode(x));
31
- let heading = text(decoded, "one");
32
- let address = text(decoded, "two");
33
- let email = node(decoded, "attributes.id", "eml");
34
- let password = node(decoded, "attributes.id", "pwd");
35
- let search = node(decoded, "attributes.id", "search");
36
- let card = node(decoded, "attributes.id", "cardnum");
37
- let option = text(decoded, "option1");
38
- let textarea = text(decoded, "textarea");
39
- let click = clicks(decoded)[0];
40
- let input = inputs(decoded)[0];
41
- let group = changes(decoded);
42
-
43
- // Non-sensitive fields continue to pass through with sensitive bits masked off
44
- assert.equal(heading, "Thanks for your order #▫▪▪▫▫▫▪▪");
45
-
46
- // Sensitive fields, including input fields, are randomized and masked
47
- assert.equal(address, "•••••• ••••• ••••• ••••• ••••• •••••");
48
- assert.equal(email.attributes.value, "••••• •••• •••• ••••");
49
- assert.equal(password.attributes.value, "••••");
50
- assert.equal(search.attributes.value, "••••• •••• ••••");
51
- assert.equal(card.attributes.value, "•••••");
52
- assert.equal(textarea, "••••• •••••");
53
- assert.equal(option, "• •••••");
54
-
55
- // Clicked text and input value should be consistent with uber masking configuration
56
- assert.equal(click.data.text, "Hello ▪▪▪▫▪");
57
- assert.equal(input.data.value, "••••• •••• •••• ••••");
58
- assert.equal(group.length, 2);
59
- // Search change - we should captured mangled input and hash
60
- assert.equal(group[0].data.type, "search");
61
- assert.equal(group[0].data.value, "••••• •••• •••• ••••");
62
- assert.equal(group[0].data.checksum, "4y7m6");
63
- // Password change - we should capture placholder value and empty hash
64
- assert.equal(group[1].data.type, "password");
65
- assert.equal(group[1].data.value, "••••");
66
- assert.equal(group[1].data.checksum, "");
67
- });
68
-
69
- it('should mask all text in strict mode', async () => {
70
- let encoded: string[] = await markup(page, "core.html", { content: false });
71
- let decoded = encoded.map(x => decode(x));
72
- let heading = text(decoded, "one");
73
- let address = text(decoded, "two");
74
- let email = node(decoded, "attributes.id", "eml");
75
- let password = node(decoded, "attributes.id", "pwd");
76
- let search = node(decoded, "attributes.id", "search");
77
- let card = node(decoded, "attributes.id", "cardnum");
78
- let click = clicks(decoded)[0];
79
- let input = inputs(decoded)[0];
80
- let option = text(decoded, "option1");
81
-
82
- // All fields are randomized and masked
83
- assert.equal(heading, "• ••••• ••••• ••••• ••••• •••••");
84
- assert.equal(address, "•••••• ••••• ••••• ••••• ••••• •••••");
85
- assert.equal(email.attributes.value, "••••• •••• •••• ••••");
86
- assert.equal(password.attributes.value, "••••");
87
- assert.equal(search.attributes.value, "••••• •••• ••••");
88
- assert.equal(card.attributes.value, "•••••");
89
- assert.equal(option, "• •••••");
90
-
91
- // Clicked text and input value should also be masked in strict mode
92
- assert.equal(click.data.text, "••••• •••• ••••");
93
- assert.equal(input.data.value, "••••• •••• •••• ••••");
94
- });
95
-
96
- it('should unmask non-sensitive text in relaxed mode', async () => {
97
- let encoded: string[] = await markup(page, "core.html", { unmask: ["body"] });
98
- let decoded = encoded.map(x => decode(x));
99
- let heading = text(decoded, "one");
100
- let address = text(decoded, "two");
101
- let email = node(decoded, "attributes.id", "eml");
102
- let password = node(decoded, "attributes.id", "pwd");
103
- let search = node(decoded, "attributes.id", "search");
104
- let card = node(decoded, "attributes.id", "cardnum");
105
- let click = clicks(decoded)[0];
106
- let input = inputs(decoded)[0];
107
- let option = text(decoded, "option1");
108
-
109
- // Text flows through unmasked for non-sensitive fields, with exception of input fields
110
- assert.equal(heading, "Thanks for your order #2AB700GH");
111
- assert.equal(address, "1 Microsoft Way, Redmond, WA - 98052");
112
- assert.equal(search.attributes.value, "••••• •••• ••••");
113
- assert.equal(option, "• •••••");
114
-
115
- // Sensitive fields are still masked
116
- assert.equal(email.attributes.value, "••••• •••• •••• ••••");
117
- assert.equal(password.attributes.value, "••••");
118
- assert.equal(card.attributes.value, "•••••");
119
-
120
- // Clicked text comes through unmasked in relaxed mode but input is still masked
121
- assert.equal(click.data.text, "Hello Wor1d");
122
- assert.equal(input.data.value, "••••• •••• •••• ••••");
123
- });
124
-
125
- it('should respect mask config even in relaxed mode', async () => {
126
- let encoded: string[] = await markup(page, "core.html", { mask: ["#mask"], unmask: ["body"] });
127
- let decoded = encoded.map(x => decode(x));
128
- let subtree = text(decoded, "child");
129
- let click = clicks(decoded)[0];
130
- let input = inputs(decoded)[0];
131
-
132
- // Masked sub-trees continue to stay masked
133
- assert.equal(subtree, "••••• •••••");
134
-
135
- // Clicked text is masked due to masked configuration and input value is also masked
136
- assert.equal(click.data.text, "••••• •••• ••••");
137
- assert.equal(input.data.value, "••••• •••• •••• ••••");
138
- });
139
- });
package/test/helper.ts DELETED
@@ -1,162 +0,0 @@
1
- import { Core, Data, decode, Interaction, Layout } from "clarity-decode";
2
- import * as fs from 'fs';
3
- import * as url from 'url';
4
- import * as path from 'path';
5
- import { Browser, Page, chromium } from 'playwright';
6
-
7
- export async function launch(): Promise<Browser> {
8
- return chromium.launch({ headless: true, args: ['--no-sandbox'] });
9
- }
10
-
11
- export async function markup(page: Page, file: string, override: Core.Config = null): Promise<string[]> {
12
- const htmlPath = path.resolve(__dirname, `./html/${file}`);
13
- const htmlFileUrl = url.pathToFileURL(htmlPath).toString();
14
- const html = fs.readFileSync(htmlPath, 'utf8');
15
- await Promise.all([
16
- page.goto(htmlFileUrl),
17
- page.waitForNavigation()
18
- ]);
19
- await page.setContent(html.replace("</body>", `
20
- <script>
21
- window.payloads = [];
22
- ${fs.readFileSync(path.resolve(__dirname, `../build/clarity.min.js`), 'utf8')};
23
- clarity("start", ${config(override)});
24
- </script>
25
- </body>
26
- `));
27
- await page.hover("#two");
28
- await page.click("#child");
29
- await page.locator('#search').fill('');
30
- await page.locator('#search').type('query with numb3rs');
31
- await page.locator('#pwd').type('p1ssw0rd');
32
- await page.locator('#eml').fill('');
33
- await page.locator('#eml').type('hello@world.com');
34
- await page.waitForFunction("payloads && payloads.length > 2");
35
- return await page.evaluate('payloads');
36
- }
37
-
38
- export function clicks(decoded: Data.DecodedPayload[]): Interaction.ClickEvent[] {
39
- let output: Interaction.ClickEvent[] = [];
40
- for (let i = decoded.length - 1; i >= 0; i--) {
41
- if (decoded[i].click) {
42
- for (let j = 0; j < decoded[i].click.length;j++)
43
- {
44
- output.push(decoded[i].click[j]);
45
- }
46
- }
47
- }
48
- return output;
49
- }
50
-
51
- export function inputs(decoded: Data.DecodedPayload[]): Interaction.InputEvent[] {
52
- let output: Interaction.InputEvent[] = [];
53
- for (let i = decoded.length - 1; i >= 0; i--) {
54
- if (decoded[i].input) {
55
- for (let j = 0; j < decoded[i].input.length;j++)
56
- {
57
- output.push(decoded[i].input[j]);
58
- }
59
- }
60
- }
61
- return output;
62
- }
63
-
64
- export function changes(decoded: Data.DecodedPayload[]): Interaction.ChangeEvent[] {
65
- let output: Interaction.ChangeEvent[] = [];
66
- for (let i = decoded.length - 1; i >= 0; i--) {
67
- if (decoded[i].change) {
68
- for (let j = 0; j < decoded[i].change.length;j++)
69
- {
70
- output.push(decoded[i].change[j]);
71
- }
72
- }
73
- }
74
- return output;
75
- }
76
-
77
- export function node(decoded: Data.DecodedPayload[], key: string, value: string | number, tag: string = null): Layout.DomData {
78
- let sub = null;
79
-
80
- // Exploding nested keys into key and sub key
81
- if (key.indexOf(".") > 0) {
82
- const parts = key.split(".");
83
- if (parts.length === 2) {
84
- key = parts[0];
85
- sub = parts[1];
86
- }
87
- }
88
-
89
- // Walking over the decoded payload to find the right match
90
- for (let i = decoded.length - 1; i >= 0; i--) {
91
- if (decoded[i].dom) {
92
- for (let j = 0; j < decoded[i].dom.length; j++) {
93
- if (decoded[i].dom[j].data) {
94
- for (let k = 0; k < decoded[i].dom[j].data.length; k++) {
95
- let d = decoded[i].dom[j].data[k];
96
- if ((sub && d[key] && d[key][sub] === value) ||
97
- (d[key] && d[key] === value)) {
98
- if ((tag && d.tag === tag) || tag === null) {
99
- return d;
100
- }
101
- }
102
- }
103
- }
104
- }
105
- }
106
- }
107
- return null;
108
- }
109
-
110
- export function text(decoded: Data.DecodedPayload[], id: string): string {
111
- let parent = node(decoded, "attributes.id", id);
112
- if (parent) {
113
- let child = node(decoded, "parent", parent.id, "*T");
114
- if (child && child.value) {
115
- return child.value;
116
- }
117
- }
118
- return null;
119
- }
120
-
121
-
122
- function config(override: Core.Config): string {
123
- const settings = {
124
- delay: 100,
125
- content: true,
126
- fraud: [],
127
- regions: [],
128
- mask: [],
129
- unmask: [],
130
- upload: payload => { window["payloads"].push(payload); window["clarity"]("upgrade", "test"); }
131
- }
132
-
133
- // Process overrides
134
- if (override){
135
- for (let key of Object.keys(override)) {
136
- settings[key] = override[key];
137
- }
138
- }
139
-
140
- // Serialize configuration
141
- let output = "";
142
- for (let key of Object.keys(settings)) {
143
- switch (key) {
144
- case "upload":
145
- output += `${JSON.stringify(key)}: ${settings[key].toString()},`;
146
- break;
147
- case "projectId":
148
- case "mask":
149
- case "unmask":
150
- case "regions":
151
- case "cookies":
152
- case "fraud":
153
- output += `${JSON.stringify(key)}: ${JSON.stringify(settings[key])},`;
154
- break;
155
- default:
156
- output += `${JSON.stringify(key)}: ${settings[key]},`;
157
- break;
158
- }
159
- }
160
- output += `"projectId": "test"`;
161
- return "{" + output + "}";
162
- }
@@ -1,27 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>Core Tests</title>
5
- <style>input, textarea, select { margin: 10px; display: block; }</style>
6
- </head>
7
- <body>
8
- <div>
9
- <h1 id="one">Thanks for your order #2AB700GH</h1>
10
- <p id="two" class="address-details">1 Microsoft Way, Redmond, WA - 98052</p>
11
- </div>
12
- <div id="mask">
13
- <span id="child">Hello Wor1d</span>
14
- </div>
15
- <form name="login">
16
- <input type="email" id="eml" title="Email" value="random@email.test">
17
- <input type="password" id="pwd" title="Password" maxlength="16" value="passw0rd">
18
- <input type="search" id="search" title="Search" value="hello w0rld">
19
- <input type="text" id="cardnum" title="CC" value="1234">
20
- <textarea id="textarea" autocapitalize="off" role="combobox" rows="5" placeholder="" spellcheck="false">Hell0 World</textarea>
21
- <select id="select" ng-options="origin.code" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
22
- <option id="option1" label="Halifax" value="string:YHZ">Halifax</option>
23
- <option id="option2" label="Montréal" value="string:YUL" selected="selected">Montréal</option>
24
- </select>
25
- </form>
26
- </body>
27
- </html>
package/test/stub.test.ts DELETED
@@ -1,7 +0,0 @@
1
- import { assert } from 'chai';
2
-
3
- describe('Stub Tests! Replace with working real tests!', () => {
4
- it('should be a successful test', () => {
5
- assert.isTrue(true);
6
- });
7
- });
@@ -1,6 +0,0 @@
1
- {
2
- "extends": "../tsconfig.json",
3
- "compilerOptions": {
4
- "module": "commonjs"
5
- }
6
- }