From 4cee1da8e21d0f3f19717cb93d5f38f5832dcc4d Mon Sep 17 00:00:00 2001 From: kitce Date: Sun, 20 Mar 2022 20:10:41 +0800 Subject: [PATCH] fix(helpers): update `merge.ts` --- jest.config.js | 5 ++++- package.json | 1 + pnpm-lock.yaml | 13 +++++++++++++ src/helpers/label.ts | 35 ++++++++++++++++++++++++++++++----- src/helpers/merge.test.ts | 31 +++++++++++++++++++++++++++++++ src/helpers/merge.ts | 20 +++++++++++--------- src/helpers/subscription.ts | 5 +++++ 7 files changed, 95 insertions(+), 15 deletions(-) diff --git a/jest.config.js b/jest.config.js index cc59fa8..9ba75d4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,5 +1,8 @@ /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { preset: 'ts-jest', - testEnvironment: 'node' + testEnvironment: 'jsdom', + moduleNameMapper: { + '\\.module\\.s?css$': 'identity-obj-proxy' + } }; diff --git a/package.json b/package.json index 72e4e79..af3bef8 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "css-loader": "^5.2.0", "cssnano": "^5.1.4", "dotenv": "^10.0.0", + "identity-obj-proxy": "^3.0.0", "jest": "^27.5.1", "postcss": "^8.2.9", "postcss-loader": "^5.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79731f8..f573fcf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,7 @@ specifiers: focus-trap-react: ^8.8.2 handsontable: ^11.1.0 html2canvas: ^1.0.0-rc.7 + identity-obj-proxy: ^3.0.0 immer: ^8.0.1 invert-color: ^2.0.0 jest: ^27.5.1 @@ -127,6 +128,7 @@ devDependencies: css-loader: 5.2.7_webpack@5.64.2 cssnano: 5.1.4_postcss@8.3.11 dotenv: 10.0.0 + identity-obj-proxy: 3.0.0 jest: 27.5.1_ts-node@9.1.1 postcss: 8.3.11 postcss-loader: 5.3.0_postcss@8.3.11+webpack@5.64.2 @@ -3878,6 +3880,10 @@ packages: har-schema: 2.0.0 dev: true + /harmony-reflect/1.6.2: + resolution: {integrity: sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==} + dev: true + /has-ansi/2.0.0: resolution: {integrity: sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=} engines: {node: '>=0.10.0'} @@ -4185,6 +4191,13 @@ packages: postcss: 8.3.11 dev: true + /identity-obj-proxy/3.0.0: + resolution: {integrity: sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=} + engines: {node: '>=4'} + dependencies: + harmony-reflect: 1.6.2 + dev: true + /immediate/3.0.6: resolution: {integrity: sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=} dev: false diff --git a/src/helpers/label.ts b/src/helpers/label.ts index fe38e00..0642cf4 100644 --- a/src/helpers/label.ts +++ b/src/helpers/label.ts @@ -1,9 +1,9 @@ import { shortenedHost } from '../constants/lihkg'; -import { IDataSet } from '../models/DataSet'; -import { IGroupedLabel } from '../types/label'; -import { IPost } from '../types/lihkg'; -import { ILabel, ISource } from './../models/Label'; -import { TTracablePost } from './../types/lihkg'; +import type { IDataSet } from '../models/DataSet'; +import type { IGroupedLabel } from '../types/label'; +import type { IPost } from '../types/lihkg'; +import type { ILabel, ISource } from './../models/Label'; +import type { TTracablePost } from './../types/lihkg'; import { counter } from './counter'; import { getShareID } from './lihkg'; @@ -63,3 +63,28 @@ export const groupByText = (user: string, dataSets: IDataSet[]) => { return groupedLabels; }, []); }; + +/** + * compare two label objects to check equality + * @param {ILabel} labelA the target A to be compare + * @param {ILabel} labelB the target B to be compare + * @param {boolean} [strict=true] enable strict equality checking + */ +export const isEqual = (labelA: ILabel, labelB: ILabel, strict = false) => { + if (labelA.id && labelB.id) { + // `id` determines everything + return labelA.id === labelB.id; + } + /** + * either one of them is stale + * attempt to compare something else + */ + return ( + labelA.text === labelB.text + && ( + strict ? ( + labelA.reason === labelB.reason + ) : true + ) + ); +}; diff --git a/src/helpers/merge.test.ts b/src/helpers/merge.test.ts index 62c3b11..9b50e21 100644 --- a/src/helpers/merge.test.ts +++ b/src/helpers/merge.test.ts @@ -135,6 +135,37 @@ describe('mergeDataSet', () => { expect(merged.data['2']![0]).toBe(dataSetB.data['2']![0]); expect(merged.data['2']![1]).toBe(dataSetB.data['2']![1]); }); + + it('should have 1 user, 3 labels', () => { + const dataSetA: IDataSet = { + data: { + '1': [ + { text: 'Label 1A', reason: 'Reason A' }, + { text: 'Label 1A', reason: 'Reason B' }, + { text: 'Label 1B' } + ] + } + }; + const dataSetB: IDataSet = { + data: { + '1': [ + { id: '1', text: 'Label 1A', reason: 'Reason A' }, + { id: '2', text: 'Label 1A', reason: 'Reason B' }, + { id: '3', text: 'Label 1B' } + ] + } + }; + const merged = mergeDataSet(dataSetA, dataSetB, true); + expect(Object.keys(merged.data).length).toBe(1); + const mergedLabels = merged.data['1']!; + expect(mergedLabels.length).toBe(3); + for (let i = 0; i < mergedLabels.length; i++) { + const mergedLabel = mergedLabels[i]; + expect(mergedLabel.id).toEqual(dataSetA.data['1']![i].id); + expect(mergedLabel.text).toEqual(dataSetB.data['1']![i].text); + expect(mergedLabel.reason).toEqual(dataSetB.data['1']![i].reason); + } + }); }); describe('mergeSubscriptions', () => { diff --git a/src/helpers/merge.ts b/src/helpers/merge.ts index 1cced60..5e6e855 100644 --- a/src/helpers/merge.ts +++ b/src/helpers/merge.ts @@ -1,6 +1,8 @@ import { produce } from 'immer'; -import { IDataSet } from '../models/DataSet'; -import { ISerializedSubscription } from './../models/Subscription'; +import type { IDataSet } from '../models/DataSet'; +import type { ISerializedSubscription } from './../models/Subscription'; +import { isEqual as isLabelEqual } from './label'; +import { isEqual as isSubscriptionEqual } from './subscription'; export enum MergeDirection { IncomingToLocal, @@ -26,16 +28,16 @@ export const mergeDataSet = (dataSetA: T, dataSetB: T, prun /** existing user in B */ if (labelsB) { if (prune) { - /** prune the missing A */ + /** prune the missing B in A */ dataA[userA] = dataA[userA]?.filter((labelA) => { - const labelB = labelsB.find((labelB) => labelB.id === labelA.id || labelB.text === labelA.text); + const labelB = labelsB.find((labelB) => isLabelEqual(labelA, labelB)); return !!labelB; }); } /** merge B into A */ const labelsA = dataA[userA]!; for (const labelB of labelsB) { - const labelA = labelsA.find((labelA) => labelA.id === labelB.id || labelA.text === labelB.text); + const labelA = labelsA.find((labelA) => isLabelEqual(labelA, labelB, true)); if (labelA) { /** existing label */ labelA.text = labelB.text; @@ -73,15 +75,15 @@ export const mergeDataSet = (dataSetA: T, dataSetB: T, prun * @returns {ISerializedSubscription[]} */ export const mergeSubscriptions = (subscriptionsA: ISerializedSubscription[], subscriptionsB: ISerializedSubscription[], prune: boolean): ISerializedSubscription[] => { - /** prune the missing A */ + /** prune the missing B in A */ const _subscriptionsA = !prune ? subscriptionsA : subscriptionsA.filter((subscriptionA) => { - const subscriptionB = subscriptionsB.find(({ url }) => url === subscriptionA.url); + const subscriptionB = subscriptionsB.find((subscriptionB) => isSubscriptionEqual(subscriptionA, subscriptionB)); return !!subscriptionB; }); return produce(_subscriptionsA, (subscriptionsA) => { /** existing subscriptions */ for (const subscriptionA of subscriptionsA) { - const subscriptionB = subscriptionsB.find((subscriptionB) => subscriptionB.url === subscriptionA.url); + const subscriptionB = subscriptionsB.find((subscriptionB) => isSubscriptionEqual(subscriptionA, subscriptionB)); if (subscriptionB) { subscriptionA.name = subscriptionB.name; subscriptionA.enabled = subscriptionB.enabled; @@ -89,7 +91,7 @@ export const mergeSubscriptions = (subscriptionsA: ISerializedSubscription[], su } /** new subscriptions */ for (const subscriptionB of subscriptionsB) { - const subscriptionA = subscriptionsA.find((subscriptionA) => subscriptionA.url === subscriptionB.url); + const subscriptionA = subscriptionsA.find((subscriptionA) => isSubscriptionEqual(subscriptionA, subscriptionB)); if (!subscriptionA) { subscriptionsA.push(subscriptionB); } diff --git a/src/helpers/subscription.ts b/src/helpers/subscription.ts index 4a5b6b2..b4aa5c7 100644 --- a/src/helpers/subscription.ts +++ b/src/helpers/subscription.ts @@ -1,6 +1,11 @@ import { SUBSCRIPTION_MESSAGE_QUESTION_ADD } from '../constants/texts'; +import type { ISerializedSubscription } from '../models/Subscription'; export const prompt = () => { const url = window.prompt(SUBSCRIPTION_MESSAGE_QUESTION_ADD); return url; }; + +export const isEqual = (subscriptionA: ISerializedSubscription, subscriptionB: ISerializedSubscription) => { + return subscriptionA.url === subscriptionB.url; +};