From b8e88f3ab1d82a32bbca3e2914befa0333a06d1a Mon Sep 17 00:00:00 2001 From: Stephane MEYER Date: Wed, 11 Sep 2024 09:34:42 +0200 Subject: [PATCH] chore: convert unit tests of xo-collection from Jest to node-core-test Jest test removed --- packages/xo-collection/package.json | 3 +- packages/xo-collection/src/collection.spec.js | 343 ----------------- packages/xo-collection/src/collection.test.js | 356 ++++++++++++++++++ .../src/{index.spec.js => index.test.js} | 37 +- ...que-index.spec.js => unique-index.test.js} | 34 +- 5 files changed, 392 insertions(+), 381 deletions(-) delete mode 100644 packages/xo-collection/src/collection.spec.js create mode 100644 packages/xo-collection/src/collection.test.js rename packages/xo-collection/src/{index.spec.js => index.test.js} (73%) rename packages/xo-collection/src/{unique-index.spec.js => unique-index.test.js} (69%) diff --git a/packages/xo-collection/package.json b/packages/xo-collection/package.json index 6489c9e85f8..e11c40b6a54 100644 --- a/packages/xo-collection/package.json +++ b/packages/xo-collection/package.json @@ -39,6 +39,7 @@ "prebuild": "rimraf dist/", "predev": "yarn run prebuild", "prepublishOnly": "yarn run build", - "postversion": "npm publish" + "postversion": "npm publish", + "test": "node --test dist/" } } diff --git a/packages/xo-collection/src/collection.spec.js b/packages/xo-collection/src/collection.spec.js deleted file mode 100644 index e636804d9ce..00000000000 --- a/packages/xo-collection/src/collection.spec.js +++ /dev/null @@ -1,343 +0,0 @@ -/* eslint-env jest */ - -import fromEvent from 'promise-toolbox/fromEvent' -import forEach from 'lodash/forEach.js' - -import { Collection, DuplicateItem, NoSuchItem } from './collection' - -// =================================================================== - -function waitTicks(n = 2) { - const { nextTick } = process - - return new Promise(function (resolve) { - ;(function waitNextTick() { - // The first tick is handled by Promise#then() - if (--n) { - nextTick(waitNextTick) - } else { - resolve() - } - })() - }) -} - -describe('Collection', function () { - let col - beforeEach(function () { - col = new Collection() - col.add('bar', 0) - - return waitTicks() - }) - - it('is iterable', function () { - const iterator = col[Symbol.iterator]() - - expect(iterator.next()).toEqual({ done: false, value: ['bar', 0] }) - expect(iterator.next()).toEqual({ done: true, value: undefined }) - }) - - describe('#keys()', function () { - it('returns an iterator over the keys', function () { - const iterator = col.keys() - - expect(iterator.next()).toEqual({ done: false, value: 'bar' }) - expect(iterator.next()).toEqual({ done: true, value: undefined }) - }) - }) - - describe('#values()', function () { - it('returns an iterator over the values', function () { - const iterator = col.values() - - expect(iterator.next()).toEqual({ done: false, value: 0 }) - expect(iterator.next()).toEqual({ done: true, value: undefined }) - }) - }) - - describe('#add()', function () { - it('adds item to the collection', function () { - const spy = jest.fn() - col.on('add', spy) - - col.add('foo', true) - - expect(col.get('foo')).toBe(true) - - // No sync events. - expect(spy).not.toHaveBeenCalled() - - // Async event. - return fromEvent(col, 'add').then(function (added) { - expect(Object.keys(added)).toEqual(['foo']) - expect(added.foo).toBe(true) - }) - }) - - it('throws an exception if the item already exists', function () { - expect(() => col.add('bar', true)).toThrowError(DuplicateItem) - }) - - it('accepts an object with an id property', function () { - const foo = { id: 'foo' } - - col.add(foo) - - expect(col.get(foo.id)).toBe(foo) - }) - }) - - describe('#update()', function () { - it('updates an item of the collection', function () { - const spy = jest.fn() - col.on('update', spy) - - col.update('bar', 1) - expect(col.get('bar')).toBe(1) // Will be forgotten by de-duplication - col.update('bar', 2) - expect(col.get('bar')).toBe(2) - - // No sync events. - expect(spy).not.toHaveBeenCalled() - - // Async event. - return fromEvent(col, 'update').then(function (updated) { - expect(Object.keys(updated)).toEqual(['bar']) - expect(updated.bar).toBe(2) - }) - }) - - it('throws an exception if the item does not exist', function () { - expect(() => col.update('baz', true)).toThrowError(NoSuchItem) - }) - - it('accepts an object with an id property', function () { - const bar = { id: 'bar' } - - col.update(bar) - - expect(col.get(bar.id)).toBe(bar) - }) - }) - - describe('#remove()', function () { - it('removes an item of the collection', function () { - const spy = jest.fn() - col.on('remove', spy) - - col.update('bar', 1) - expect(col.get('bar')).toBe(1) // Will be forgotten by de-duplication - col.remove('bar') - - // No sync events. - expect(spy).not.toHaveBeenCalled() - - // Async event. - return fromEvent(col, 'remove').then(function (removed) { - expect(Object.keys(removed)).toEqual(['bar']) - expect(removed.bar).toBeUndefined() - }) - }) - - it('throws an exception if the item does not exist', function () { - expect(() => col.remove('baz', true)).toThrowError(NoSuchItem) - }) - - it('accepts an object with an id property', function () { - const bar = { id: 'bar' } - - col.remove(bar) - - expect(col.has(bar.id)).toBe(false) - }) - }) - - describe('#set()', function () { - it('adds item if collection has not key', function () { - const spy = jest.fn() - col.on('add', spy) - - col.set('foo', true) - - expect(col.get('foo')).toBe(true) - - // No sync events. - expect(spy).not.toHaveBeenCalled() - - // Async events. - return fromEvent(col, 'add').then(function (added) { - expect(Object.keys(added)).toEqual(['foo']) - expect(added.foo).toBe(true) - }) - }) - - it('updates item if collection has key', function () { - const spy = jest.fn() - col.on('udpate', spy) - - col.set('bar', 1) - - expect(col.get('bar')).toBe(1) - - // No sync events. - expect(spy).not.toHaveBeenCalled() - - // Async events. - return fromEvent(col, 'update').then(function (updated) { - expect(Object.keys(updated)).toEqual(['bar']) - expect(updated.bar).toBe(1) - }) - }) - - it('accepts an object with an id property', function () { - const foo = { id: 'foo' } - - col.set(foo) - - expect(col.get(foo.id)).toBe(foo) - }) - }) - - describe('#unset()', function () { - it('removes an existing item', function () { - col.unset('bar') - - expect(col.has('bar')).toBe(false) - - return fromEvent(col, 'remove').then(function (removed) { - expect(Object.keys(removed)).toEqual(['bar']) - expect(removed.bar).toBeUndefined() - }) - }) - - it('does not throw if the item does not exists', function () { - col.unset('foo') - }) - - it('accepts an object with an id property', function () { - col.unset({ id: 'bar' }) - - expect(col.has('bar')).toBe(false) - - return fromEvent(col, 'remove').then(function (removed) { - expect(Object.keys(removed)).toEqual(['bar']) - expect(removed.bar).toBeUndefined() - }) - }) - }) - - describe('touch()', function () { - it('can be used to signal an indirect update', function () { - const foo = { id: 'foo' } - col.add(foo) - - return waitTicks().then(() => { - col.touch(foo) - - return fromEvent(col, 'update', items => { - expect(Object.keys(items)).toEqual(['foo']) - expect(items.foo).toBe(foo) - }) - }) - }) - }) - - describe('clear()', function () { - it('removes all items from the collection', function () { - col.clear() - - expect(col.size).toBe(0) - - return fromEvent(col, 'remove').then(items => { - expect(Object.keys(items)).toEqual(['bar']) - expect(items.bar).toBeUndefined() - }) - }) - }) - - describe('deduplicates events', function () { - forEach( - { - 'add & update → add': [ - [ - ['add', 'foo', 0], - ['update', 'foo', 1], - ], - { - add: { - foo: 1, - }, - }, - ], - - 'add & remove → ∅': [ - [ - ['add', 'foo', 0], - ['remove', 'foo'], - ], - {}, - ], - - 'update & update → update': [ - [ - ['update', 'bar', 1], - ['update', 'bar', 2], - ], - { - update: { - bar: 2, - }, - }, - ], - - 'update & remove → remove': [ - [ - ['update', 'bar', 1], - ['remove', 'bar'], - ], - { - remove: { - bar: undefined, - }, - }, - ], - - 'remove & add → update': [ - [ - ['remove', 'bar'], - ['add', 'bar', 0], - ], - { - update: { - bar: 0, - }, - }, - ], - }, - ([operations, results], label) => { - it(label, function () { - forEach(operations, ([method, ...args]) => { - col[method](...args) - }) - - const spies = Object.create(null) - forEach(['add', 'update', 'remove'], event => { - col.on(event, (spies[event] = jest.fn())) - }) - - return waitTicks().then(() => { - forEach(spies, (spy, event) => { - const items = results[event] - if (items) { - expect(spy.mock.calls).toEqual([[items]]) - } else { - expect(spy).not.toHaveBeenCalled() - } - }) - }) - }) - } - ) - }) -}) diff --git a/packages/xo-collection/src/collection.test.js b/packages/xo-collection/src/collection.test.js new file mode 100644 index 00000000000..cf7672e6a9b --- /dev/null +++ b/packages/xo-collection/src/collection.test.js @@ -0,0 +1,356 @@ +import fromEvent from 'promise-toolbox/fromEvent' +import forEach from 'lodash/forEach.js' + +import assert from 'assert' +import { test, describe, beforeEach } from 'node:test' + +import { Collection, DuplicateItem, NoSuchItem } from './collection' + +// =================================================================== + +function waitTicks(n = 2) { + const { nextTick } = process + + return new Promise(function (resolve) { + ;(function waitNextTick() { + if (--n) { + nextTick(waitNextTick) + } else { + resolve() + } + })() + }) +} + +describe('Collection', () => { + let col + beforeEach(() => { + col = new Collection() + col.add('bar', 0) + + return waitTicks() + }) + + test('is iterable', () => { + const iterator = col[Symbol.iterator]() + + assert.deepStrictEqual(iterator.next(), { done: false, value: ['bar', 0] }) + assert.deepStrictEqual(iterator.next(), { done: true, value: undefined }) + }) + + describe('#keys()', () => { + test('returns an iterator over the keys', () => { + const iterator = col.keys() + + assert.deepStrictEqual(iterator.next(), { done: false, value: 'bar' }) + assert.deepStrictEqual(iterator.next(), { done: true, value: undefined }) + }) + }) + + describe('#values()', () => { + test('returns an iterator over the values', () => { + const iterator = col.values() + + assert.deepStrictEqual(iterator.next(), { done: false, value: 0 }) + assert.deepStrictEqual(iterator.next(), { done: true, value: undefined }) + }) + }) + + describe('#add()', () => { + test('adds item to the collection', async () => { + let called = false + col.on('add', () => { + called = true + }) + + col.add('foo', true) + assert.strictEqual(col.get('foo'), true) + + // No sync events. + assert.strictEqual(called, false) + + // Async event. + return fromEvent(col, 'add').then(function (added) { + assert.deepStrictEqual(Object.keys(added), ['foo']) + assert.strictEqual(added.foo, true) + }) + }) + + test('throws an exception if the item already exists', () => { + assert.throws(() => col.add('bar', true), DuplicateItem) + }) + + test('accepts an object with an id property', () => { + const foo = { id: 'foo' } + + col.add(foo) + + assert.strictEqual(col.get(foo.id), foo) + }) + }) + + describe('#update()', () => { + test('updates an item of the collection', async () => { + let called = false + col.on('update', () => { + called = true + }) + + col.update('bar', 1) + assert.strictEqual(col.get('bar'), 1) + col.update('bar', 2) + assert.strictEqual(col.get('bar'), 2) + + // No sync events. + assert.strictEqual(called, false) + + // Async event. + return fromEvent(col, 'update').then(function (updated) { + assert.deepStrictEqual(Object.keys(updated), ['bar']) + assert.strictEqual(updated.bar, 2) + }) + }) + + test('throws an exception if the item does not exist', () => { + assert.throws(() => col.update('baz', true), NoSuchItem) + }) + + test('accepts an object with an id property', () => { + const bar = { id: 'bar' } + + col.update(bar) + + assert.strictEqual(col.get(bar.id), bar) + }) + }) + + describe('#remove()', () => { + test('removes an item of the collection', async () => { + let called = false + col.on('remove', () => { + called = true + }) + + col.update('bar', 1) + assert.strictEqual(col.get('bar'), 1) // Will be forgotten by de-duplication + col.remove('bar') + + // No sync events. + assert.strictEqual(called, false) + + // Async event. + return fromEvent(col, 'remove').then(function (removed) { + assert.deepStrictEqual(Object.keys(removed), ['bar']) + assert.strictEqual(removed.bar, undefined) + }) + }) + + test('throws an exception if the item does not exist', () => { + assert.throws(() => col.remove('baz', true), NoSuchItem) + }) + + test('accepts an object with an id property', () => { + const bar = { id: 'bar' } + col.remove(bar) + assert.strictEqual(col.has(bar.id), false) + }) + }) + + describe('#set()', () => { + test('adds item if collection has no key', async () => { + let called = false + col.on('add', () => { + called = true + }) + + col.set('foo', true) + assert.strictEqual(col.get('foo'), true) + + // No sync events. + assert.strictEqual(called, false) + + // Async events. + return fromEvent(col, 'add').then(function (added) { + assert.deepStrictEqual(Object.keys(added), ['foo']) + assert.strictEqual(added.foo, true) + }) + }) + + test('updates item if collection has key', async () => { + let called = false + col.on('update', () => { + called = true + }) + + col.set('bar', 1) + assert.strictEqual(col.get('bar'), 1) + + // No sync events. + assert.strictEqual(called, false) + + // Async events. + return fromEvent(col, 'update').then(function (updated) { + assert.deepStrictEqual(Object.keys(updated), ['bar']) + assert.strictEqual(updated.bar, 1) + }) + }) + + test('accepts an object with an id property', () => { + const foo = { id: 'foo' } + + col.set(foo) + + assert.strictEqual(col.get(foo.id), foo) + }) + }) + + describe('#unset()', () => { + test('removes an existing item', async () => { + col.unset('bar') + assert.strictEqual(col.has('bar'), false) + + return fromEvent(col, 'remove').then(function (removed) { + assert.deepStrictEqual(Object.keys(removed), ['bar']) + assert.strictEqual(removed.bar, undefined) + }) + }) + + test('does not throw if the item does not exist', () => { + col.unset('foo') + }) + + test('accepts an object with an id property', async () => { + col.unset({ id: 'bar' }) + assert.strictEqual(col.has('bar'), false) + + return fromEvent(col, 'remove').then(function (removed) { + assert.deepStrictEqual(Object.keys(removed), ['bar']) + assert.strictEqual(removed.bar, undefined) + }) + }) + }) + + describe('#touch()', () => { + test('can be used to signal an indirect update', async () => { + const foo = { id: 'foo' } + col.add(foo) + + return waitTicks().then(() => { + col.touch(foo) + + return fromEvent(col, 'update', items => { + assert.deepStrictEqual(Object.keys(items), ['foo']) + assert.strictEqual(items.foo, foo) + }) + }) + }) + }) + + describe('#clear()', () => { + test('removes all items from the collection', async () => { + col.clear() + + assert.strictEqual(col.size, 0) + + return fromEvent(col, 'remove').then(items => { + assert.deepStrictEqual(Object.keys(items), ['bar']) + assert.strictEqual(items.bar, undefined) + }) + }) + }) + + describe('deduplicates events', () => { + forEach( + { + 'add & update → add': [ + [ + ['add', 'foo', 0], + ['update', 'foo', 1], + ], + { + add: { + foo: 1, + }, + }, + ], + + 'add & remove → ∅': [ + [ + ['add', 'foo', 0], + ['remove', 'foo'], + ], + {}, + ], + + 'update & update → update': [ + [ + ['update', 'bar', 1], + ['update', 'bar', 2], + ], + { + update: { + bar: 2, + }, + }, + ], + + 'update & remove → remove': [ + [ + ['update', 'bar', 1], + ['remove', 'bar'], + ], + { + remove: { + bar: undefined, + }, + }, + ], + + 'remove & add → update': [ + [ + ['remove', 'bar'], + ['add', 'bar', 0], + ], + { + update: { + bar: 0, + }, + }, + ], + }, + ([operations, results], label) => { + test(label, function () { + forEach(operations, ([method, ...args]) => { + col[method](...args) + }) + + const spies = Object.create(null) + forEach(['add', 'update', 'remove'], event => { + spies[event] = { + calls: [], + fn: function (...args) { + this.calls.push(...args) + }, + } + col.on(event, spies[event].fn.bind(spies[event])) + }) + + return waitTicks().then(() => { + // console.log("Captured events:", spies); + forEach(spies, (spy, event) => { + const items = [results[event]] + .map(r => Object.assign(Object.create(null), r)) + .filter(r => Object.keys(r).length > 0) + + if (items) { + assert.deepStrictEqual(spy.calls, items, `${event} should have been called with the correct arguments`) + } else { + assert.strictEqual(spy.calls.length, 0, `${event} should not have been called`) + } + }) + }) + }) + } + ) + }) +}) diff --git a/packages/xo-collection/src/index.spec.js b/packages/xo-collection/src/index.test.js similarity index 73% rename from packages/xo-collection/src/index.spec.js rename to packages/xo-collection/src/index.test.js index e7ba3e28252..1c8cd8c4576 100644 --- a/packages/xo-collection/src/index.spec.js +++ b/packages/xo-collection/src/index.test.js @@ -1,10 +1,9 @@ -/* eslint-env jest */ - -import fromEvent from 'promise-toolbox/fromEvent' -import forEach from 'lodash/forEach.js' - -import { Collection } from './collection' -import { Index } from './index' +const fromEvent = require('promise-toolbox/fromEvent') +const forEach = require('lodash/forEach.js') +const { Collection } = require('./collection') +const { Index } = require('./index') +const assert = require('assert') +const { describe, test, beforeEach } = require('node:test') // =================================================================== @@ -56,8 +55,8 @@ describe('Index', function () { return waitTicks() }) - it('works with existing items', function () { - expect(col.indexes).toEqual({ + test('works with existing items', function () { + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byGroup: { foo: { [item1.id]: item1, @@ -70,7 +69,7 @@ describe('Index', function () { }) }) - it('works with added items', function () { + test('works with added items', function () { const item5 = { id: '823b56c4-4b96-4f3a-9533-5d08177167ac', group: 'baz', @@ -79,7 +78,7 @@ describe('Index', function () { col.add(item5) return waitTicks().then(() => { - expect(col.indexes).toEqual({ + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byGroup: { foo: { [item1.id]: item1, @@ -96,7 +95,7 @@ describe('Index', function () { }) }) - it('works with updated items', function () { + test('works with updated items', function () { const item1bis = { id: item1.id, group: 'bar', @@ -105,7 +104,7 @@ describe('Index', function () { col.update(item1bis) return waitTicks().then(() => { - expect(col.indexes).toEqual({ + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byGroup: { foo: { [item3.id]: item3, @@ -119,11 +118,11 @@ describe('Index', function () { }) }) - it('works with removed items', function () { + test('works with removed items', function () { col.remove(item2) return waitTicks().then(() => { - expect(col.indexes).toEqual({ + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byGroup: { foo: { [item1.id]: item1, @@ -135,7 +134,7 @@ describe('Index', function () { }) }) - it('correctly updates the value even the same object has the same hash', function () { + test('correctly updates the value even if the same object has the same hash', function () { const item1bis = { id: item1.id, group: item1.group, @@ -145,7 +144,7 @@ describe('Index', function () { col.update(item1bis) return fromEvent(col, 'finish').then(() => { - expect(col.indexes).toEqual({ + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byGroup: { foo: { [item1.id]: item1bis, @@ -160,13 +159,13 @@ describe('Index', function () { }) describe('#sweep()', function () { - it('removes empty items lists', function () { + test('removes empty items lists', function () { col.remove(item2) return waitTicks().then(() => { byGroup.sweep() - expect(col.indexes).toEqual({ + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byGroup: { foo: { [item1.id]: item1, diff --git a/packages/xo-collection/src/unique-index.spec.js b/packages/xo-collection/src/unique-index.test.js similarity index 69% rename from packages/xo-collection/src/unique-index.spec.js rename to packages/xo-collection/src/unique-index.test.js index c6e9e42da42..b240d76c3f7 100644 --- a/packages/xo-collection/src/unique-index.spec.js +++ b/packages/xo-collection/src/unique-index.test.js @@ -1,10 +1,9 @@ -/* eslint-env jest */ - -import fromEvent from 'promise-toolbox/fromEvent' -import forEach from 'lodash/forEach.js' - -import { Collection } from './collection' -import { UniqueIndex } from './unique-index' +const fromEvent = require('promise-toolbox/fromEvent') +const forEach = require('lodash/forEach.js') +const { Collection } = require('./collection') +const { UniqueIndex } = require('./unique-index') +const assert = require('assert') +const { describe, test, beforeEach } = require('node:test') // =================================================================== @@ -46,14 +45,13 @@ describe('UniqueIndex', function () { }) byKey = new UniqueIndex('key') - col.createIndex('byKey', byKey) return waitTicks() }) - it('works with existing items', function () { - expect(col.indexes).toEqual({ + test('works with existing items', function () { + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byKey: { [item1.key]: item1, [item2.key]: item2, @@ -61,7 +59,7 @@ describe('UniqueIndex', function () { }) }) - it('works with added items', function () { + test('works with added items', function () { const item4 = { id: '823b56c4-4b96-4f3a-9533-5d08177167ac', key: '1437af14-429a-40db-8a51-8a2f5ed03201', @@ -70,7 +68,7 @@ describe('UniqueIndex', function () { col.add(item4) return waitTicks().then(() => { - expect(col.indexes).toEqual({ + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byKey: { [item1.key]: item1, [item2.key]: item2, @@ -80,7 +78,7 @@ describe('UniqueIndex', function () { }) }) - it('works with updated items', function () { + test('works with updated items', function () { const item1bis = { id: item1.id, key: 'e03d4a3a-0331-4aca-97a2-016bbd43a29b', @@ -89,7 +87,7 @@ describe('UniqueIndex', function () { col.update(item1bis) return waitTicks().then(() => { - expect(col.indexes).toEqual({ + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byKey: { [item1bis.key]: item1bis, [item2.key]: item2, @@ -98,11 +96,11 @@ describe('UniqueIndex', function () { }) }) - it('works with removed items', function () { + test('works with removed items', function () { col.remove(item2) return waitTicks().then(() => { - expect(col.indexes).toEqual({ + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byKey: { [item1.key]: item1, }, @@ -110,7 +108,7 @@ describe('UniqueIndex', function () { }) }) - it('correctly updates the value even the same object has the same hash', function () { + test('correctly updates the value even if the same object has the same hash', function () { const item1bis = { id: item1.id, key: item1.key, @@ -120,7 +118,7 @@ describe('UniqueIndex', function () { col.update(item1bis) return fromEvent(col, 'finish').then(() => { - expect(col.indexes).toEqual({ + assert.deepStrictEqual(JSON.parse(JSON.stringify(col.indexes)), { byKey: { [item1.key]: item1bis, [item2.key]: item2,