diff --git a/src/features/dataset/index.spec.ts b/src/features/dataset/index.spec.ts
index fc48953..83d456c 100644
--- a/src/features/dataset/index.spec.ts
+++ b/src/features/dataset/index.spec.ts
@@ -54,6 +54,7 @@ describe('dataset', () => {
});
it('update if the source value changes', () => {
+ // Arrange
const [data, setData] = createSignal([
{ id: '1', name: 'a first name', amount: 30 },
{ id: '2', name: 'a second name', amount: 20 },
@@ -61,28 +62,25 @@ describe('dataset', () => {
]);
const dataset = createDataSet(data);
- dataset.mutateEach(item => ({ ...item, amount: item.amount * 2 }))
+ dataset.mutateEach(item => ({ ...item, amount: item.amount * 2 }));
+ // Act
+ setData([
+ { id: '4', name: 'a first name', amount: 30 },
+ { id: '5', name: 'a second name', amount: 20 },
+ { id: '6', name: 'a third name', amount: 10 },
+ ]);
+
+ // Assert
return testEffect(done =>
- createEffect((run: number = 0) => {
- data();
+ createEffect(() => {
+ expect(dataset.value).toEqual([
+ { id: '4', name: 'a first name', amount: 60 },
+ { id: '5', name: 'a second name', amount: 40 },
+ { id: '6', name: 'a third name', amount: 20 },
+ ])
- if (run === 0) {
- setData([
- { id: '4', name: 'a first name', amount: 30 },
- { id: '5', name: 'a second name', amount: 20 },
- { id: '6', name: 'a third name', amount: 10 },
- ]);
- } else if (run === 1) {
- expect(dataset.value).toEqual([
- { id: '4', name: 'a first name', amount: 60 },
- { id: '5', name: 'a second name', amount: 40 },
- { id: '6', name: 'a third name', amount: 20 },
- ])
-
- done()
- }
- return run + 1
+ done()
})
);
});
diff --git a/src/features/source/source.spec.ts b/src/features/source/source.spec.ts
new file mode 100644
index 0000000..5d5a4a4
--- /dev/null
+++ b/src/features/source/source.spec.ts
@@ -0,0 +1,49 @@
+import { describe, expect } from "vitest";
+import { createSource } from "./source";
+import { it } from "~/test-helpers";
+import { testEffect } from "@solidjs/testing-library";
+import { createEffect, createSignal } from "solid-js";
+
+describe('Source', () => {
+ describe('Source', () => {
+ it('should return a `Source`', () => {
+ // Arrange
+
+ // Act
+ const actual = createSource('');
+
+ // Assert
+ expect(actual.out).toBe('');
+ });
+
+ it('should transform the input format to output format', () => {
+ // Arrange
+ const given = '**text**\n';
+ const expected = '
text
';
+
+ // Act
+ const actual = createSource(given);
+
+ // Assert
+ expect(actual.out).toBe(expected);
+ });
+
+ it('should contain query results', () => {
+ // Arrange
+ const expected: [number, number][] = [[8, 9], [12, 13], [15, 16]];
+ const source = createSource('this is a seachable string');
+
+ // Act
+ source.query = 'a';
+
+ // Assert
+ return testEffect(done => {
+ createEffect(() => {
+ expect(source.queryResults).toEqual(expected);
+
+ done()
+ });
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/features/source/source.ts b/src/features/source/source.ts
index f5d2fcc..439f5e8 100644
--- a/src/features/source/source.ts
+++ b/src/features/source/source.ts
@@ -37,16 +37,12 @@ const inToOutProcessor = unified().use(remarkParse).use(remarkRehype).use(rehype
const outToInProcessor = unified().use(rehypeParse).use(rehypeRemark).use(remarkStringify, { bullet: '-' });
export function createSource(initalValue: string): Source {
- const [store, setStore] = createStore({ in: initalValue, out: '', plain: '', query: '', metadata: { spellingErrors: [], grammarErrors: [], queryResults: [] } });
- onMount(() => {
- const ast = inToOutProcessor.runSync(inToOutProcessor.parse(initalValue));
+ const ast = inToOutProcessor.runSync(inToOutProcessor.parse(initalValue));
+ const out = String(inToOutProcessor.stringify(ast));
+ const plain = String(unified().use(plainTextStringify).stringify(ast));
- setStore({
- out: String(inToOutProcessor.stringify(ast)),
- plain: String(unified().use(plainTextStringify).stringify(ast)),
- });
- });
+ const [store, setStore] = createStore({ in: initalValue, out, plain, query: '', metadata: { spellingErrors: [], grammarErrors: [], queryResults: [] } });
createEffect(() => {
const value = store.plain;
diff --git a/src/utilities.spec.ts b/src/utilities.spec.ts
index 2a1bc3e..ee7cb90 100644
--- a/src/utilities.spec.ts
+++ b/src/utilities.spec.ts
@@ -1,23 +1,9 @@
-import { afterAll, beforeEach, describe, expect, vi } from 'vitest';
+import { describe, expect, vi } from 'vitest';
import { decode, deepCopy, deepDiff, filter, gen__split_by_filter, map, MutarionKind, split_by_filter, splitAt } from './utilities';
-import { install } from '@sinonjs/fake-timers';
import { it } from '~/test-helpers';
const { spyOn } = vi;
-type MilliSeconds = number;
-const useFakeTimers = () => {
- const clock = install();
-
- beforeEach(() => clock.reset());
- afterAll(() => clock.uninstall());
-
- return {
- tick(timeToAdvance: MilliSeconds) {
- clock.tick(timeToAdvance);
- },
- };
-};
const first = (iterable: Iterable): T | undefined => {
for (const value of iterable) {
return value;
@@ -126,6 +112,18 @@ describe('utilities', () => {
expect(actual).toBe(expected);
});
+ it('should decode \\b characters', async () => {
+ // Arrange
+ const given = 'this is\\ba string';
+ const expected = 'this is\ba string';
+
+ // Act
+ const actual = decode(given);
+
+ // Assert
+ expect(actual).toBe(expected);
+ });
+
it('should decode \\n characters', async () => {
// Arrange
const given = 'this is\\na string';
@@ -138,6 +136,54 @@ describe('utilities', () => {
expect(actual).toBe(expected);
});
+ it('should decode \\r characters', async () => {
+ // Arrange
+ const given = 'this is\\ra string';
+ const expected = 'this is\ra string';
+
+ // Act
+ const actual = decode(given);
+
+ // Assert
+ expect(actual).toBe(expected);
+ });
+
+ it('should decode \\f characters', async () => {
+ // Arrange
+ const given = 'this is\\fa string';
+ const expected = 'this is\fa string';
+
+ // Act
+ const actual = decode(given);
+
+ // Assert
+ expect(actual).toBe(expected);
+ });
+
+ it('should decode \' characters', async () => {
+ // Arrange
+ const given = 'this is\\\'a string';
+ const expected = 'this is\'a string';
+
+ // Act
+ const actual = decode(given);
+
+ // Assert
+ expect(actual).toBe(expected);
+ });
+
+ it('should decode \" characters', async () => {
+ // Arrange
+ const given = 'this is\"a string';
+ const expected = 'this is"a string';
+
+ // Act
+ const actual = decode(given);
+
+ // Assert
+ expect(actual).toBe(expected);
+ });
+
it('should decode \\uHHHH characters', async () => {
// Arrange
const given = 'this is \\u1234 a string';
@@ -301,6 +347,18 @@ describe('utilities', () => {
expect(actual).toEqual({ kind: MutarionKind.Create, key: 'key', value: 'value' });
});
+ it('should yield a mutation of type create when `b` contains a value that `a` does not', async () => {
+ // arrange
+ const a = { key: null };
+ const b = { key: 'value' };
+
+ // Act
+ const actual = first(deepDiff(a, b));
+
+ // Arrange
+ expect(actual).toEqual({ kind: MutarionKind.Create, key: 'key', value: 'value' });
+ });
+
it('should yield a mutation of type delete when `a` contains a key that `b` does not', async () => {
// arrange
const a = { key: 'value' };
@@ -313,6 +371,18 @@ describe('utilities', () => {
expect(actual).toEqual({ kind: MutarionKind.Delete, key: 'key', original: 'value' });
});
+ it('should yield a mutation of type delete when `a` contains a key that `b` does not', async () => {
+ // arrange
+ const a = { key: 'value' };
+ const b = { key: undefined };
+
+ // Act
+ const actual = first(deepDiff(a, b));
+
+ // Arrange
+ expect(actual).toEqual({ kind: MutarionKind.Delete, key: 'key', original: 'value' });
+ });
+
it('should yield a mutation of type update when the value of a key in `a` is not equal to the value of the same key in `b`', async () => {
// arrange
const a = { key: 'old' };
diff --git a/src/utilities.ts b/src/utilities.ts
index 024898f..421d8a0 100644
--- a/src/utilities.ts
+++ b/src/utilities.ts
@@ -27,8 +27,11 @@ export function split_by_filter(subject: string, filter: string): (readonly [boo
return Array.from(gen__split_by_filter(subject, filter));
}
+type Hex = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 'a' | 'b' | 'c' | 'd' | 'e' | 'f';
+type EncodedChar = 't' | 'b' | 'n' | 'r' | 'f' | '\'' | '"' | `u${Hex}${Hex | ''}${Hex | ''}${Hex | ''}`
+
const decodeRegex = /(? ({
+const decodeReplacer = (_: any, char: EncodedChar) => ({
t: '\t',
b: '\b',
n: '\n',
@@ -37,7 +40,7 @@ const decodeReplacer = (_: any, char: string) => ({
"'": '\'',
'"': '\"',
u: String.fromCharCode(Number.parseInt(`0x${char.slice(1)}`)),
-}[char.charAt(0)] ?? '');
+}[char.charAt(0) as ('t' | 'b' | 'n' | 'r' | 'f' | '\'' | '"' | 'u')]);
export const decode = (subject: string): string => subject.replace(decodeRegex, decodeReplacer);
export const deepCopy = (original: T): T => {
@@ -110,8 +113,13 @@ export function* deepDiff(a: T1, b: T2, pa
const key = path.concat(keyA!.toString()).join('.');
yield ((): Mutation => {
- if (valueA === null || valueA === undefined) return { key, kind: MutarionKind.Create, value: valueB };
- if (valueB === null || valueB === undefined) return { key, kind: MutarionKind.Delete, original: valueA };
+ if (valueA === null || valueA === undefined) {
+ return { key, kind: MutarionKind.Create, value: valueB };
+ }
+
+ if (valueB === null || valueB === undefined) {
+ return { key, kind: MutarionKind.Delete, original: valueA };
+ }
return { key, kind: MutarionKind.Update, value: valueB, original: valueA };
})();
@@ -200,7 +208,7 @@ const bufferredIterator = (subject: I
const next = () => {
const res = iterator.next();
- done = res.done ?? false;
+ done = res.done!;
if (!done) {
buffer.push(res.value);