diff --git a/README.md b/README.md index b428130..d9e2d3f 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ Filter all the console old logs and show only the last one ### Event Highlighting -The event highlighting allows to visualize the active events within a mini notation pattern. This means, that only events within quotation marks will be considered. +The event highlighting allows to visualize the active events within a mini notation pattern. This means, that only events within quotation marks will be considered. #### TidalCycles configuration @@ -105,10 +105,6 @@ tidal <- startStream (defaultConfig {cFrameTimespan = 1/50}) [(superdirtTarget { The path to the `BootTidal.hs` file can be found in the TidalCycles output console after TidalCycles has been booted in the editor. -#### Framerate - -The event highlight animation is in relation to the refresh rate of the users display and the `cFrameTimespan` value of TidalCycles. This means, that the animation fps needs to be smaller then the denominator of the `cFrameTimespan` value. However a good value is somehow between `20 fps` and `30 fps`. - #### Custom Styles It is possible to customize the event highlighting css styles. For this you can add the css classes under `Pulsar -> Stylesheet...`. @@ -133,7 +129,7 @@ And it is possible to override the styles for every individual stream like this: The pattern of the css class is `.event-highlight-[streamID]`. ### Osc Eval -It's possibile to evaluate tidal code with OSC messages. +It's possible to evaluate tidal code with OSC messages. #### Port The plugin is listening on this specified port for incoming osc messages: diff --git a/lib/config.js b/lib/config.js index 9d193df..d43ddf5 100644 --- a/lib/config.js +++ b/lib/config.js @@ -138,12 +138,6 @@ export default { description: 'Check to enable event highlighting.', order: 95 }, - 'fps': { - type: 'number', - default: 30, - description: 'Reduce this value if the event highlighting flickers. Higher values make the result smoother, but require more computational capacity and it is limited by the cFrameTimespan value configured in TidalCycles. It is recommended to use a value between 20 and 30 fps.', - order: 100, - }, 'ip': { type: 'string', default: "127.0.0.1", diff --git a/lib/event-highlighting/event-highlight-worker.js b/lib/event-highlighting/event-highlight-worker.js new file mode 100644 index 0000000..db8cc0a --- /dev/null +++ b/lib/event-highlighting/event-highlight-worker.js @@ -0,0 +1,41 @@ +const EventMapHelper = require('./event-map-helper') + +require("../polyfills/set"); + +const FRAME_RATE = 1000/30; + +let messageBuffer = new Map(); +let receivedThisFrame = new Map(); + +self.onmessage = function(e) { + const event = e.data; + + queueEvent(event); +}; + +setInterval(() => { + const {active, added, removed} = EventMapHelper.diffEventMaps( + messageBuffer, + receivedThisFrame, + ); + + postMessage({active, added, removed }); + messageBuffer = EventMapHelper.setToMap(added.union(active)); + receivedThisFrame.clear(); + +}, FRAME_RATE); + +/** Helper that creates (or returns existing) nested maps. */ +function ensureNestedMap(root, key) { + if (!root.has(key)) root.set(key, new Map()); + return root.get(key); +} + +/** Store events until the next animation frame */ +function queueEvent(event) { + const recvMap = ensureNestedMap(receivedThisFrame, event.eventId); + + if (!recvMap.has(event.colStart)) { + recvMap.set(event.colStart, event); + } +} diff --git a/lib/event-highlighter.js b/lib/event-highlighting/event-highlighter.js similarity index 69% rename from lib/event-highlighter.js rename to lib/event-highlighting/event-highlighter.js index bafef97..74fdf21 100644 --- a/lib/event-highlighter.js +++ b/lib/event-highlighting/event-highlighter.js @@ -1,7 +1,8 @@ 'use babel' -import OscServer from './osc-server'; +import OscServer from './../osc-server'; import LineProcessor from './line-processor'; +import path from 'path'; const CLASS = Object.freeze({ base: "event-highlight", @@ -28,16 +29,8 @@ export default class EventHighlighter { // Data‑structures ------------------------------------------------------- this.markers = new Map(); // textbuffer.id → row → col → Marker this.highlights = new Map(); // eventId → texteditor.id → col → Marker - this.filteredMessages = new Map(); // eventId → event - this.receivedThisFrame = new Map(); // eventId → event - this.addedThisFrame = new Set(); // Set this.eventIds = []; // [{textbufferId, row}] - - // Animation state ------------------------------------------------------- - this.then = 0; // time at previous frame - - // Bind instance methods used as callbacks ----------------------------- - this.animate = this.animate.bind(this); + this.worker; } // ---------------------------------------------------------------------- @@ -48,9 +41,21 @@ export default class EventHighlighter { init() { this.#installBaseHighlightStyle(); - // Kick‑off animation loop - this.then = window.performance.now(); - requestAnimationFrame(this.animate); + this.#startWorker(); + } + + #startWorker() { + const workerPath = path.join(__dirname, '.', 'event-highlight-worker.js'); + this.worker = new Worker(workerPath); + + this.worker.onmessage = function(event) { + const {added, removed} = event.data; + added.forEach((evt) => { + this.#addHighlight(evt); + }); + + removed.forEach((evt) => this.#removeHighlight(evt)); + }.bind(this); } /** Clean‑up resources when package is deactivated */ @@ -88,7 +93,10 @@ export default class EventHighlighter { oscHighlightSubscriber() { return (args: {}): void => { const message = OscServer.asDictionary(this.highlightTransformer(args)); - this.#queueEvent(message); + + if (this.worker) { + this.worker.postMessage(message); + } } } @@ -105,40 +113,9 @@ export default class EventHighlighter { return result; } - /** requestAnimationFrame callback */ - animate(now) { - const elapsed = now - this.then; - const configFPS = atom.config.get('tidalcycles.eventHighlighting.fps'); - const fpsInterval = 1000 / configFPS; - - if (elapsed >= fpsInterval) { - this.then = now - (elapsed % fpsInterval); - - // Add newly‑received highlights ------------------------------------- - this.addedThisFrame.forEach((evt) => { - this.#addHighlight(evt); - }); - - // Remove highlights no longer present ------------------------------ - const { updated, removed } = this.#diffEventMaps( - this.filteredMessages, - this.receivedThisFrame, - ); - this.filteredMessages = updated; - removed.forEach((evt) => this.#removeHighlight(evt)); - - // Reset per‑frame collections -------------------------------------- - this.receivedThisFrame.clear(); - this.addedThisFrame.clear(); - } - - requestAnimationFrame(this.animate); - } - // ---------------------------------------------------------------------- // Private helpers // ---------------------------------------------------------------------- - /** Injects the base CSS rule used for all highlights */ #installBaseHighlightStyle() { atom.styles.addStyleSheet(` @@ -149,18 +126,6 @@ export default class EventHighlighter { `); } - /** Store events until the next animation frame */ - #queueEvent(event) { - const eventMap = ensureNestedMap(this.filteredMessages, event.eventId); - const recvMap = ensureNestedMap(this.receivedThisFrame, event.eventId); - - if (!eventMap.has(event.colStart)) eventMap.set(event.colStart, event); - if (!recvMap.has(event.colStart)) { - this.addedThisFrame.add(event); - recvMap.set(event.colStart, event); - } - } - // Highlight management #addHighlight({ id, colStart, eventId }) { const bufferId = this.eventIds[eventId].bufferId; @@ -201,7 +166,7 @@ export default class EventHighlighter { const highlightEvents = this.highlights.get(eventId); // console.log("removeHighlight", highlightEvents, eventId, colStart); - if (!highlightEvents.size) return; + if (!highlightEvents || !highlightEvents.size) return; highlightEvents.forEach(textEditorIdEvent => { const marker = textEditorIdEvent.get(colStart); @@ -210,8 +175,6 @@ export default class EventHighlighter { if (!marker) return; marker.destroy(); }) - - } // Marker generation (per line) @@ -234,26 +197,4 @@ export default class EventHighlighter { }); } - #diffEventMaps(prevEvents, currentEvents) { - const removed = new Set(); - const updated = new Map(prevEvents); - - for (const [event, prevCols] of prevEvents) { - const currCols = currentEvents.get(event); - if (!currCols) { - for (const [, prevEvt] of prevCols) removed.add(prevEvt); - updated.delete(event); - continue; - } - - for (const [col, prevEvt] of prevCols) { - if (!currCols.has(col)) { - removed.add(prevEvt); - updated.get(event).delete(col); - } - } - } - - return { updated, removed }; - } } diff --git a/lib/event-highlighting/event-map-helper.js b/lib/event-highlighting/event-map-helper.js new file mode 100644 index 0000000..c6ff02e --- /dev/null +++ b/lib/event-highlighting/event-map-helper.js @@ -0,0 +1,59 @@ +'use babel' + +class EventMapHelper { + + static diffEventMaps(prevEvents, currentEvents) { + const removed = new Set(); + const added = new Set(); + const active = new Set(); + + for (const [event, prevCols] of prevEvents) { + const currCols = currentEvents.get(event); + if (!currCols) { + for (const [, prevEvt] of prevCols) removed.add(prevEvt); + continue; + } + + for (const [col, prevEvt] of prevCols) { + if (!currCols.has(col)) { + removed.add(prevEvt); + } else { + active.add(prevEvt); + } + } + } + + for (const [event, currCols] of currentEvents) { + const prevCols = prevEvents.get(event); + if (!prevCols) { + for (const [, currEvt] of currCols) added.add(currEvt); + continue; + } + + for (const [col, currEvt] of currCols) { + if (!prevCols.has(col)) { + added.add(currEvt); + } + } + } + + return { removed, added, active}; + } + + static setToMap(events) { + const resultEvents = new Map(); + events.forEach(event => { + if (!resultEvents.get(event.eventId)) { + resultEvents.set(event.eventId, new Map()); + } + const cols = resultEvents.get(event.eventId); + + cols.set(event.colStart, event); + }); + + return resultEvents; + } +} + + +module.exports = EventMapHelper; diff --git a/lib/line-processor.js b/lib/event-highlighting/line-processor.js similarity index 63% rename from lib/line-processor.js rename to lib/event-highlighting/line-processor.js index 58e0b7f..5cc9f51 100644 --- a/lib/line-processor.js +++ b/lib/event-highlighting/line-processor.js @@ -4,10 +4,10 @@ export default class LineProcessor { // Valid TidalCycles word chars static isValidTidalWordChar(character) { - const code = character.charCodeAt(0); - // 0-9 - const digitMin = 48; - const digitMax = 57; + const code = character.charCodeAt(0); + // 0-9 + const digitMin = 48; + const digitMax = 57; // A-Z const upperCaseMin = 65; const upperCaseMax = 90; @@ -15,19 +15,19 @@ export default class LineProcessor { const lowerCaseMin = 97; const lowerCaseMax = 122; - return ( - (code >= digitMin && code <= digitMax) || - (code >= upperCaseMin && code <= upperCaseMax) || - (code >= lowerCaseMin && code <= lowerCaseMax) - ); + return ( + (code >= digitMin && code <= digitMax) || + (code >= upperCaseMin && code <= upperCaseMax) || + (code >= lowerCaseMin && code <= lowerCaseMax) + ); } - + static isQuotationMark(character) { - const code = character.charCodeAt(0); + const code = character.charCodeAt(0); // " const quotationMark = 34; - return code === quotationMark; + return code === quotationMark; } static findTidalWordRanges(line, callback) { @@ -38,7 +38,7 @@ export default class LineProcessor { Array.from(line).forEach((char, index) => { if (LineProcessor.isQuotationMark(char)) { insideQuotes = !insideQuotes; - } + } if (insideQuotes && LineProcessor.isValidTidalWordChar(char)) { if (!start) { @@ -54,14 +54,14 @@ export default class LineProcessor { end = null; } } - }) + }) } static controlPatternsRegex() { - return new RegExp(/"([^"]*)"/g); + return new RegExp(/"([^"]*)"/g); } static exceptedFunctionPatterns() { - return new RegExp(/numerals\s*=.*$|p\s.*$/); + return new RegExp(/numerals\s*=.*$|p\s.*$/); } } diff --git a/lib/osc-eval.js b/lib/osc-eval.js index 4a48903..4a13587 100644 --- a/lib/osc-eval.js +++ b/lib/osc-eval.js @@ -2,11 +2,12 @@ import Repl from './repl'; import {Editors} from './editors'; +import OscServer from "./osc-server"; export function oscEvalSubscriber(tidalRepl: Repl, editors: Editors) { return (args: {}): void => { const message = OscServer.asDictionary(args); - + if (message['tab'] !== undefined) { atom.workspace.getPanes()[0].setActiveItem(atom.workspace.getTextEditors()[message['tab']]) } @@ -15,6 +16,8 @@ export function oscEvalSubscriber(tidalRepl: Repl, editors: Editors) { editors.goTo(message['row'] - 1, message['column']) } - tidalRepl.eval(message['type'], false); + if (message['type']) { + tidalRepl.eval(message['type'], false); + } } } diff --git a/lib/polyfills/set.js b/lib/polyfills/set.js new file mode 100644 index 0000000..efca6ba --- /dev/null +++ b/lib/polyfills/set.js @@ -0,0 +1,5 @@ +if (!Set.prototype.union) { + Set.prototype.union = function (otherSet) { + return new Set([...this, ...otherSet]) + } +} diff --git a/lib/tidalcycles.js b/lib/tidalcycles.js index 6187dbb..989618a 100644 --- a/lib/tidalcycles.js +++ b/lib/tidalcycles.js @@ -8,12 +8,13 @@ import AutocompleteProvider from './autocomplete-provider'; import Ghc from './ghc'; import BootTidal from './boot-tidal'; import SoundBrowser from './sound-browser'; -import EventHighlighter from "./event-highlighter"; +import EventHighlighter from "./event-highlighting/event-highlighter"; import OscServer from "./osc-server"; const Status = require('./status') const SuperDirt = require('./superdirt') const { LINE, MULTI_LINE, WHOLE_EDITOR, Editors } = require('./editors') const config = require('./config') +require('./polyfills/set') export default { consoleView: null, diff --git a/package-lock.json b/package-lock.json index 6ac5f09..410e4af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "tidalcycles", - "version": "4.0.2", + "version": "4.1.0", "license": "GPL-3.0", "dependencies": { "directory-tree": "^3.5.2", diff --git a/spec/event-highlighting/event-map-helper.spec.js b/spec/event-highlighting/event-map-helper.spec.js new file mode 100644 index 0000000..bfe3601 --- /dev/null +++ b/spec/event-highlighting/event-map-helper.spec.js @@ -0,0 +1,87 @@ + +const EventMapHelper = require("../../lib/event-highlighting/event-map-helper"); + +require("../../lib/polyfills/set"); + +/* TEST DATA */ +const messageBuffer = new Map([ + [20, new Map([ + [8, {id: 1, colStart: 8, eventId: 20}], + [32, {id: 1, colStart: 32, eventId: 20}] + ])], + [33, new Map([ + [10, {id: 2, colStart: 10, eventId: 33}], + ])] +]); + +const receivedMessagesThisFrame = new Map([ + [20, new Map([ + [8, {id: 1, colStart: 8, eventId: 20}], + ])], + [33, new Map([ + [10, {id: 2, colStart: 10, eventId: 33}], + ])], + [50, new Map([ + [24, {id: 3, colStart: 24, eventId: 50}], + ])] +]); + +describe('Event Map Helper', () => { + + describe('diffEventMaps', () => { + + it('should return the correct differences for deleted events', () => { + const {removed} = EventMapHelper.diffEventMaps(messageBuffer,receivedMessagesThisFrame); + + expectedResult = new Set([ {id: 1, colStart: 32, eventId: 20 }]); + + expect([...removed]).toEqual([...expectedResult]); + }) + + it('should return the correct differences for added events', () => { + const {added} = EventMapHelper.diffEventMaps(messageBuffer,receivedMessagesThisFrame); + + expectedResult = new Set([ {id: 3, colStart: 24, eventId: 50 }]); + + expect([...added]).toEqual([...expectedResult]); + }) + + it('should return the correct differences for active events', () => { + const {active} = EventMapHelper.diffEventMaps(messageBuffer,receivedMessagesThisFrame); + + const expectedResult = new Set([ + { id: 1, colStart: 8, eventId: 20 } + , { id: 2, colStart: 10, eventId: 33 } + ]); + + expect([...active]).toEqual([...expectedResult]); + + }) + }) + + describe('setToMap', () => { + it('should return the setToMap Sets into correspondings Maps', () => { + const events = new Set([ + { id: 1, colStart: 8, eventId: 20 } + , { id: 2, colStart: 10, eventId: 33 } + , { id: 3, colStart: 24, eventId: 50 } + ]); + + const setToMap = EventMapHelper.setToMap(events); + + const expectedResult = new Map([ + [20, new Map([ + [8, {id: 1, colStart: 8, eventId: 20}] + ])], + [33, new Map([ + [10, {id: 2, colStart: 10, eventId: 33}] + ])], + [50, new Map([ + [24, {id: 3, colStart: 24, eventId: 50}] + ])] + ]); + + expect(setToMap).toEqual(expectedResult); + }) + }) +}) diff --git a/spec/event-highlighting/line-processor-spec.js b/spec/event-highlighting/line-processor-spec.js new file mode 100644 index 0000000..7fa8d1a --- /dev/null +++ b/spec/event-highlighting/line-processor-spec.js @@ -0,0 +1,101 @@ +const LineProcessor = require("../../lib/event-highlighting/line-processor"); + +describe('Line Processor', () => { + + describe('isValidTidalWordChar', () => { + it('should truthify a valid digit', () => { + expect(LineProcessor.isValidTidalWordChar('5')).toBe(true); + }) + it('should truthify a valid upper case char', () => { + expect(LineProcessor.isValidTidalWordChar('M')).toBe(true); + }) + + it('should truthify a valid lower case char', () => { + expect(LineProcessor.isValidTidalWordChar('t')).toBe(true); + }) + + it('should falsify an invalid char', () => { + expect(LineProcessor.isValidTidalWordChar('*')).toBe(false); + }) + }) + + describe('isQuotationMark', () => { + it('should truthify quotation mark', () => { + expect(LineProcessor.isQuotationMark('"')).toBe(true); + }) + + it('should falsify non quotation mark', () => { + expect(LineProcessor.isQuotationMark('*')).toBe(false); + }) + }) + + describe('isQuotationMark', () => { + it('should find the range for one ControlPattern and one word and execute the callback once', () => { + const results = []; + LineProcessor.findTidalWordRanges( + `d1 $ s "superpiano" # note 0`, + (result) => results.push(result)); + + expect(results.length).toEqual(1); + expect(results[0]).toEqual({ start: 8, end: 17}); + }) + + it('should find the range for two ControlPatterns and several words and execute the callback accorgingly', () => { + const results = []; + LineProcessor.findTidalWordRanges( + `d1 $ s "" # note "0"`, + (result) => results.push(result)); + + expect(results.length).toEqual(3); + expect(results[0]).toEqual({ start: 9, end: 18}); + expect(results[1]).toEqual({ start: 20, end: 22}); + expect(results[2]).toEqual({ start: 34, end: 34}); + }) + + + it('should find the range for one relatively complex ControlPattern and several words and execute the callback accordingly', () => { + const results = []; + LineProcessor.findTidalWordRanges( + `d1 $ s "superpiano" # note "c'maj'4*<1 2 3>"`, + (result) => results.push(result)); + + expect(results.length).toEqual(7); + expect(results[0]).toEqual({ start: 8, end: 17}); + expect(results[1]).toEqual({ start: 28, end: 28}); + expect(results[2]).toEqual({ start: 30, end: 32}); + expect(results[3]).toEqual({ start: 34, end: 34}); + expect(results[4]).toEqual({ start: 37, end: 37}); + expect(results[5]).toEqual({ start: 39, end: 39}); + expect(results[6]).toEqual({ start: 41, end: 41}); + }) + }) + + describe('controlPatternsRegex', () => { + it ('should match all strings and their quotation marks in a line', () => { + const testString = `d1 $ s "" # note "0"`; + const expected = [`""`, `"0"`]; + + expect(testString.match(LineProcessor.controlPatternsRegex())).toEqual(expected); + }) + }) + + describe('exceptedFunctionPatterns', () => { + it ('should match numerals function occurance in a line', () => { + const testString = `numerals = "0 1 2 3"`; + const expected = 'numerals = "0 1 2 3"'; + expect(testString.match(LineProcessor.exceptedFunctionPatterns())[0]).toEqual(expected); + }) + + it ('should match p function occurance in a line', () => { + const testString = `p "hello" $ s "808"`; + const expected = 'p "hello" $ s "808"'; + + expect(testString.match(LineProcessor.exceptedFunctionPatterns())[0]).toEqual(expected); + }) + + it ('should not match an allowed control pattern in a line', () => { + const testString = `d1 $ s "superpiano"`; + expect(testString.match(LineProcessor.exceptedFunctionPatterns())).toBeNull() + }) + }) +}) diff --git a/spec/line-processor-spec.js b/spec/line-processor-spec.js deleted file mode 100644 index a8366fe..0000000 --- a/spec/line-processor-spec.js +++ /dev/null @@ -1,101 +0,0 @@ -const LineProcessor = require("../lib/line-processor"); - -describe('Line Processor', () => { - - describe('isValidTidalWordChar', () => { - it('should truthify a valid digit', () => { - expect(LineProcessor.isValidTidalWordChar('5')).toBe(true); - }) - it('should truthify a valid upper case char', () => { - expect(LineProcessor.isValidTidalWordChar('M')).toBe(true); - }) - - it('should truthify a valid lower case char', () => { - expect(LineProcessor.isValidTidalWordChar('t')).toBe(true); - }) - - it('should falsify an invalid char', () => { - expect(LineProcessor.isValidTidalWordChar('*')).toBe(false); - }) - }) - - describe('isQuotationMark', () => { - it('should truthify quotation mark', () => { - expect(LineProcessor.isQuotationMark('"')).toBe(true); - }) - - it('should falsify non quotation mark', () => { - expect(LineProcessor.isQuotationMark('*')).toBe(false); - }) - }) - - describe('isQuotationMark', () => { - it('should find the range for one ControlPattern and one word and execute the callback once', () => { - const results = []; - LineProcessor.findTidalWordRanges( - `d1 $ s "superpiano" # note 0`, - (result) => results.push(result)); - - expect(results.length).toEqual(1); - expect(results[0]).toEqual({ start: 8, end: 17}); - }) - - it('should find the range for two ControlPatterns and several words and execute the callback accorgingly', () => { - const results = []; - LineProcessor.findTidalWordRanges( - `d1 $ s "" # note "0"`, - (result) => results.push(result)); - - expect(results.length).toEqual(3); - expect(results[0]).toEqual({ start: 9, end: 18}); - expect(results[1]).toEqual({ start: 20, end: 22}); - expect(results[2]).toEqual({ start: 34, end: 34}); - }) - - - it('should find the range for one relatively complex ControlPattern and several words and execute the callback accordingly', () => { - const results = []; - LineProcessor.findTidalWordRanges( - `d1 $ s "superpiano" # note "c'maj'4*<1 2 3>"`, - (result) => results.push(result)); - - expect(results.length).toEqual(7); - expect(results[0]).toEqual({ start: 8, end: 17}); - expect(results[1]).toEqual({ start: 28, end: 28}); - expect(results[2]).toEqual({ start: 30, end: 32}); - expect(results[3]).toEqual({ start: 34, end: 34}); - expect(results[4]).toEqual({ start: 37, end: 37}); - expect(results[5]).toEqual({ start: 39, end: 39}); - expect(results[6]).toEqual({ start: 41, end: 41}); - }) - }) - - describe('controlPatternsRegex', () => { - it ('should match all strings and their quotation marks in a line', () => { - const testString = `d1 $ s "" # note "0"`; - const expected = [`""`, `"0"`]; - - expect(testString.match(LineProcessor.controlPatternsRegex())).toEqual(expected); - }) - }) - - describe('exceptedFunctionPatterns', () => { - it ('should match numerals function occurance in a line', () => { - const testString = `numerals = "0 1 2 3"`; - const expected = 'numerals = "0 1 2 3"'; - expect(testString.match(LineProcessor.exceptedFunctionPatterns())[0]).toEqual(expected); - }) - - it ('should match p function occurance in a line', () => { - const testString = `p "hello" $ s "808"`; - const expected = 'p "hello" $ s "808"'; - - expect(testString.match(LineProcessor.exceptedFunctionPatterns())[0]).toEqual(expected); - }) - - it ('should not match an allowed control pattern in a line', () => { - const testString = `d1 $ s "superpiano"`; - expect(testString.match(LineProcessor.exceptedFunctionPatterns())).toBeNull() - }) - }) -}) diff --git a/spec/polyfills/set.spec.js b/spec/polyfills/set.spec.js new file mode 100644 index 0000000..00dda9a --- /dev/null +++ b/spec/polyfills/set.spec.js @@ -0,0 +1,25 @@ +require("../../lib/polyfills/set"); + +describe('Set', () => { + it('should apply union for simple number type correctly', () => { + const setA = new Set([1,2,3]); + const setB = new Set([3,4]); + + const result = new Set([1,2,3,4]); + + expect(setA.union(setB)).toEqual(result); + }) + + it('should apply union for objects correctly', () => { + const setA = new Set([{id: 1, colStart: 8, eventId: 20},{id: 1, colStart: 32, eventId: 20}]); + const setB = new Set([{id: 2, colStart: 10, eventId: 33}]); + + const result = new Set([ + {id: 1, colStart: 8, eventId: 20} + , {id: 1, colStart: 32, eventId: 20} + , {id: 2, colStart: 10, eventId: 33} + ]); + + expect([...setA.union(setB)]).toEqual([...result]); + }) +})