From cb82fc6e9270a62b87ddf83c58329c68a7384002 Mon Sep 17 00:00:00 2001 From: Alexander Vogt Date: Wed, 28 Aug 2024 08:42:11 +0200 Subject: [PATCH 1/5] Test comparison table filter --- .../ComparisonTableFilter.test.ts | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts diff --git a/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts b/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts new file mode 100644 index 000000000..4e8fa4d87 --- /dev/null +++ b/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts @@ -0,0 +1,165 @@ +import ComparisonTableFilter from '@/components/ComparisonTableFilter.vue' +import { flushPromises, mount } from '@vue/test-utils' +import { describe, it, vi, expect, beforeAll } from 'vitest' +import { store } from '@/stores/store' +import { MetricType } from '@/model/MetricType.ts' +import ButtonComponent from '@/components/ButtonComponent.vue' +import OptionsSelector from '@/components/optionsSelectors/OptionsSelectorComponent.vue' +import OptionComponent from '@/components/optionsSelectors/OptionComponent.vue' + +const store = { + state: { + anonymous: new Set() + }, + uiState: { + comparisonTableSortingMetric: MetricType.AVERAGE, + comparisonTableClusterSorting: false + }, + getSubmissionIds: ['A', 'B', 'C', 'test User'] +} + +describe('ComparisonTableFilter', async () => { + beforeAll(() => { + vi.mock('@/stores/store', () => ({ + store: vi.fn(() => { + return store + }) + })) + }) + + it('Test search string updating', async () => { + const wrapper = mount(ComparisonTableFilter, { + props: { + searchString: '', + 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + } + }) + + const searchValue = 'JPlag' + + wrapper.find('input').setValue(searchValue) + await flushPromises() + expect(wrapper.props('searchString')).toBe(searchValue) + }) + + it('Test metric changes', async () => { + const wrapper = mount(ComparisonTableFilter) + + expect(wrapper.text()).toContain('Average') + expect(wrapper.text()).toContain('Maximum') + expect(wrapper.text()).toContain('Cluster') + + const options = wrapper.getComponent(OptionsSelector).findAllComponents(OptionComponent) + + expectHighlighting(0) + + await options[1].trigger('click') + expect(store.uiState.comparisonTableSortingMetric).toBe(MetricType.MAXIMUM) + expect(store.uiState.comparisonTableClusterSorting).toBeFalsy() + expectHighlighting(1) + + await options[2].trigger('click') + expect(store.uiState.comparisonTableSortingMetric).toBe(MetricType.AVERAGE) + expect(store.uiState.comparisonTableClusterSorting).toBeTruthy() + expectHighlighting(2) + + await options[0].trigger('click') + expect(store.uiState.comparisonTableSortingMetric).toBe(MetricType.AVERAGE) + expect(store.uiState.comparisonTableClusterSorting).toBeFalsy() + expectHighlighting(0) + + function expectHighlighting(index: number) { + for (let i = 0; i < options.length; i++) { + if (i == index) { + expect(options[i].classes()).toContain('!bg-accent') + } else { + expect(options[i].classes()).not.toContain('!bg-accent') + } + } + } + }) + + it('Test anonymous button', async () => { + const wrapper = mount(ComparisonTableFilter) + + expect(wrapper.text()).toContain('Hide All') + + await wrapper.getComponent(ButtonComponent).trigger('click') + expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length) + for (const id of store.getSubmissionIds) { + expect(store.state.anonymous).toContain(id) + } + + // Vue does not actually rerender the component, so this is commented out + //expect(wrapper.text()).toContain('Show All') + //expect(wrapper.text()).not.toContain('Hide All') + + await wrapper.getComponent(ButtonComponent).trigger('click') + expect(store.state.anonymous.size).toBe(0) + }) + + it('Test deanoymization', async () => { + const wrapper = mount(ComparisonTableFilter, { + props: { + searchString: '', + 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + } + }) + store.state.anonymous = new Set(store.getSubmissionIds) + + wrapper.find('input').setValue('C') + expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length - 1) + expect(store.state.anonymous).not.toContain('C') + }) + + it('Test deanoymization - case insensitive', async () => { + const wrapper = mount(ComparisonTableFilter, { + props: { + searchString: '', + 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + } + }) + store.state.anonymous = new Set(store.getSubmissionIds) + + wrapper.find('input').setValue('c') + expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length - 1) + expect(store.state.anonymous).not.toContain('C') + }) + + it('Test deanoymization - multiple', async () => { + const wrapper = mount(ComparisonTableFilter, { + props: { + searchString: '', + 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + } + }) + store.state.anonymous = new Set(store.getSubmissionIds) + + wrapper.find('input').setValue('c A') + expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length - 2) + expect(store.state.anonymous).not.toContain('C') + expect(store.state.anonymous).not.toContain('A') + }) + + it('Test deanoymization - name with spaces', async () => { + const wrapper = mount(ComparisonTableFilter, { + props: { + searchString: '', + 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + } + }) + store.state.anonymous = new Set(store.getSubmissionIds) + + wrapper.find('input').setValue('test') + expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length) + expect(store.state.anonymous).toContain('test User') + + wrapper.find('input').setValue('User') + expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length) + expect(store.state.anonymous).toContain('test User') + + wrapper.find('input').setValue('test User') + expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length - 1) + expect(store.state.anonymous).not.toContain('test User') + }) +}) From 18d1d4fb5e4c2a4f15b1f777cb478d1877a4de11 Mon Sep 17 00:00:00 2001 From: Alexander Vogt Date: Wed, 28 Aug 2024 12:36:16 +0200 Subject: [PATCH 2/5] use correct pinia --- report-viewer/package-lock.json | 81 +++++++---- report-viewer/package.json | 1 + .../ComparisonTableFilter.test.ts | 129 +++++++++++------- 3 files changed, 135 insertions(+), 76 deletions(-) diff --git a/report-viewer/package-lock.json b/report-viewer/package-lock.json index e328ce47b..8872d2f01 100644 --- a/report-viewer/package-lock.json +++ b/report-viewer/package-lock.json @@ -26,6 +26,7 @@ "vue-virtual-scroller": "^2.0.0-beta.8" }, "devDependencies": { + "@pinia/testing": "^0.1.5", "@playwright/test": "^1.46.0", "@rushstack/eslint-patch": "^1.10.4", "@types/jsdom": "^21.1.7", @@ -816,6 +817,47 @@ "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "dev": true }, + "node_modules/@pinia/testing": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-0.1.5.tgz", + "integrity": "sha512-AcGzuotkzhRoF00htuxLfIPBBHVE6HjjB3YC5Y3os8vRgKu6ipknK5GBQq9+pduwYQhZ+BcCZDC9TyLAUlUpoQ==", + "dev": true, + "dependencies": { + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "pinia": ">=2.2.1" + } + }, + "node_modules/@pinia/testing/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3531,21 +3573,6 @@ "node": ">=16.17.0" } }, - "node_modules/husky": { - "version": "9.1.5", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.5.tgz", - "integrity": "sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==", - "dev": true, - "bin": { - "husky": "bin.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -5211,12 +5238,12 @@ } }, "node_modules/pinia": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz", - "integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.2.2.tgz", + "integrity": "sha512-ja2XqFWZC36mupU4z1ZzxeTApV7DOw44cV4dhQ9sGwun+N89v/XP7+j7q6TanS1u1tdbK4r+1BUx7heMaIdagA==", "dependencies": { - "@vue/devtools-api": "^6.5.0", - "vue-demi": ">=0.14.5" + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" }, "funding": { "url": "https://github.com/sponsors/posva" @@ -5236,9 +5263,9 @@ } }, "node_modules/pinia/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", "hasInstallScript": true, "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", @@ -6114,6 +6141,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/sortablejs": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz", + "integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA==", + "peer": true + }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -6724,7 +6757,7 @@ "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/report-viewer/package.json b/report-viewer/package.json index 2b8648b06..7133e1d16 100644 --- a/report-viewer/package.json +++ b/report-viewer/package.json @@ -37,6 +37,7 @@ "vue-virtual-scroller": "^2.0.0-beta.8" }, "devDependencies": { + "@pinia/testing": "^0.1.5", "@playwright/test": "^1.46.0", "@rushstack/eslint-patch": "^1.10.4", "@types/jsdom": "^21.1.7", diff --git a/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts b/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts index 4e8fa4d87..24c06c240 100644 --- a/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts +++ b/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts @@ -1,39 +1,25 @@ import ComparisonTableFilter from '@/components/ComparisonTableFilter.vue' import { flushPromises, mount } from '@vue/test-utils' -import { describe, it, vi, expect, beforeAll } from 'vitest' +import { describe, it, vi, expect } from 'vitest' +import { createTestingPinia } from '@pinia/testing' import { store } from '@/stores/store' import { MetricType } from '@/model/MetricType.ts' import ButtonComponent from '@/components/ButtonComponent.vue' import OptionsSelector from '@/components/optionsSelectors/OptionsSelectorComponent.vue' import OptionComponent from '@/components/optionsSelectors/OptionComponent.vue' -const store = { - state: { - anonymous: new Set() - }, - uiState: { - comparisonTableSortingMetric: MetricType.AVERAGE, - comparisonTableClusterSorting: false - }, - getSubmissionIds: ['A', 'B', 'C', 'test User'] -} - describe('ComparisonTableFilter', async () => { - beforeAll(() => { - vi.mock('@/stores/store', () => ({ - store: vi.fn(() => { - return store - }) - })) - }) - it('Test search string updating', async () => { const wrapper = mount(ComparisonTableFilter, { props: { searchString: '', 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + }, + global: { + plugins: [createTestingPinia({ createSpy: vi.fn })] } }) + setUpStore() const searchValue = 'JPlag' @@ -43,7 +29,12 @@ describe('ComparisonTableFilter', async () => { }) it('Test metric changes', async () => { - const wrapper = mount(ComparisonTableFilter) + const wrapper = mount(ComparisonTableFilter, { + global: { + plugins: [createTestingPinia({ createSpy: vi.fn })] + } + }) + setUpStore() expect(wrapper.text()).toContain('Average') expect(wrapper.text()).toContain('Maximum') @@ -54,18 +45,18 @@ describe('ComparisonTableFilter', async () => { expectHighlighting(0) await options[1].trigger('click') - expect(store.uiState.comparisonTableSortingMetric).toBe(MetricType.MAXIMUM) - expect(store.uiState.comparisonTableClusterSorting).toBeFalsy() + expect(store().uiState.comparisonTableSortingMetric).toBe(MetricType.MAXIMUM) + expect(store().uiState.comparisonTableClusterSorting).toBeFalsy() expectHighlighting(1) await options[2].trigger('click') - expect(store.uiState.comparisonTableSortingMetric).toBe(MetricType.AVERAGE) - expect(store.uiState.comparisonTableClusterSorting).toBeTruthy() + expect(store().uiState.comparisonTableSortingMetric).toBe(MetricType.AVERAGE) + expect(store().uiState.comparisonTableClusterSorting).toBeTruthy() expectHighlighting(2) await options[0].trigger('click') - expect(store.uiState.comparisonTableSortingMetric).toBe(MetricType.AVERAGE) - expect(store.uiState.comparisonTableClusterSorting).toBeFalsy() + expect(store().uiState.comparisonTableSortingMetric).toBe(MetricType.AVERAGE) + expect(store().uiState.comparisonTableClusterSorting).toBeFalsy() expectHighlighting(0) function expectHighlighting(index: number) { @@ -80,22 +71,30 @@ describe('ComparisonTableFilter', async () => { }) it('Test anonymous button', async () => { - const wrapper = mount(ComparisonTableFilter) + const wrapper = mount(ComparisonTableFilter, { + global: { + plugins: [createTestingPinia({ createSpy: vi.fn })] + } + }) + setUpStore() + await wrapper.vm.$nextTick() expect(wrapper.text()).toContain('Hide All') await wrapper.getComponent(ButtonComponent).trigger('click') - expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length) - for (const id of store.getSubmissionIds) { - expect(store.state.anonymous).toContain(id) + + expect(store().state.anonymous.size).toBe(store().getSubmissionIds.length) + for (const id of store().getSubmissionIds) { + expect(store().state.anonymous).toContain(id) } // Vue does not actually rerender the component, so this is commented out - //expect(wrapper.text()).toContain('Show All') - //expect(wrapper.text()).not.toContain('Hide All') + await wrapper.vm.$nextTick() + expect(wrapper.text()).toContain('Show All') + expect(wrapper.text()).not.toContain('Hide All') await wrapper.getComponent(ButtonComponent).trigger('click') - expect(store.state.anonymous.size).toBe(0) + expect(store().state.anonymous.size).toBe(0) }) it('Test deanoymization', async () => { @@ -103,13 +102,16 @@ describe('ComparisonTableFilter', async () => { props: { searchString: '', 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + }, + global: { + plugins: [createTestingPinia({ createSpy: vi.fn })] } }) - store.state.anonymous = new Set(store.getSubmissionIds) + setUpStore(true) wrapper.find('input').setValue('C') - expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length - 1) - expect(store.state.anonymous).not.toContain('C') + expect(store().state.anonymous.size).toBe(store().getSubmissionIds.length - 1) + expect(store().state.anonymous).not.toContain('C') }) it('Test deanoymization - case insensitive', async () => { @@ -117,13 +119,16 @@ describe('ComparisonTableFilter', async () => { props: { searchString: '', 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + }, + global: { + plugins: [createTestingPinia({ createSpy: vi.fn })] } }) - store.state.anonymous = new Set(store.getSubmissionIds) + setUpStore(true) wrapper.find('input').setValue('c') - expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length - 1) - expect(store.state.anonymous).not.toContain('C') + expect(store().state.anonymous.size).toBe(store().getSubmissionIds.length - 1) + expect(store().state.anonymous).not.toContain('C') }) it('Test deanoymization - multiple', async () => { @@ -131,35 +136,55 @@ describe('ComparisonTableFilter', async () => { props: { searchString: '', 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + }, + global: { + plugins: [createTestingPinia({ createSpy: vi.fn })] } }) - store.state.anonymous = new Set(store.getSubmissionIds) + setUpStore(true) wrapper.find('input').setValue('c A') - expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length - 2) - expect(store.state.anonymous).not.toContain('C') - expect(store.state.anonymous).not.toContain('A') + expect(store().state.anonymous.size).toBe(store().getSubmissionIds.length - 2) + expect(store().state.anonymous).not.toContain('C') + expect(store().state.anonymous).not.toContain('A') }) - it('Test deanoymization - name with spaces', async () => { + // skipped, because of #1946 + it.skip('Test deanoymization - name with spaces', async () => { const wrapper = mount(ComparisonTableFilter, { props: { searchString: '', 'onUpdate:searchString': (e) => wrapper.setProps({ searchString: e }) + }, + global: { + plugins: [createTestingPinia({ createSpy: vi.fn })] } }) - store.state.anonymous = new Set(store.getSubmissionIds) + setUpStore(true) wrapper.find('input').setValue('test') - expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length) - expect(store.state.anonymous).toContain('test User') + expect(store().state.anonymous.size).toBe(store().getSubmissionIds.length) + expect(store().state.anonymous).toContain('test_User') wrapper.find('input').setValue('User') - expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length) - expect(store.state.anonymous).toContain('test User') + expect(store().state.anonymous.size).toBe(store().getSubmissionIds.length) + expect(store().state.anonymous).toContain('test_User') wrapper.find('input').setValue('test User') - expect(store.state.anonymous.size).toBe(store.getSubmissionIds.length - 1) - expect(store.state.anonymous).not.toContain('test User') + expect(store().state.anonymous.size).toBe(store().getSubmissionIds.length - 1) + expect(store().state.anonymous).not.toContain('test_User') }) }) + +function setUpStore(fillAnonymous = false) { + const submissionsToDisplayNames = new Map() + submissionsToDisplayNames.set('A', 'A') + submissionsToDisplayNames.set('B', 'B') + submissionsToDisplayNames.set('C', 'C') + submissionsToDisplayNames.set('test_User', 'test User') + store().state.fileIdToDisplayName = submissionsToDisplayNames + store().state.anonymous.clear() + if (fillAnonymous) { + store().state.anonymous = new Set(submissionsToDisplayNames.keys()) + } +} From 64dfdff2853c621283fbc595f9af438073ac9ee3 Mon Sep 17 00:00:00 2001 From: Alexander Vogt Date: Thu, 29 Aug 2024 07:56:07 +0200 Subject: [PATCH 3/5] base of table tests --- .../comparisonTable/ComparisonTable.test.ts | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 report-viewer/tests/unit/components/comparisonTable/ComparisonTable.test.ts diff --git a/report-viewer/tests/unit/components/comparisonTable/ComparisonTable.test.ts b/report-viewer/tests/unit/components/comparisonTable/ComparisonTable.test.ts new file mode 100644 index 000000000..530ee6341 --- /dev/null +++ b/report-viewer/tests/unit/components/comparisonTable/ComparisonTable.test.ts @@ -0,0 +1,109 @@ +import ComparisonTable from '@/components/ComparisonsTable.vue' +import { flushPromises, mount } from '@vue/test-utils' +import { describe, it, vi, expect } from 'vitest' +import { createTestingPinia } from '@pinia/testing' +import { store } from '@/stores/store' +import { MetricType } from '@/model/MetricType.ts' +import { router } from '@/router' + +describe('ComparisonTable', async () => { + it('Test search string filtering', async () => { + const wrapper = mount(ComparisonTable, { + props: { + topComparisons: [], + clusters: [] + }, + global: { + plugins: [createTestingPinia({ createSpy: vi.fn })] + } + }) + + const searchValue = 'JPlag' + + wrapper.find('input').setValue(searchValue) + await flushPromises() + }) + + it('Test header prop', async () => { + const headerText = 'Custom Header' + + const wrapper = mount(ComparisonTable, { + props: { + topComparisons: [], + clusters: [], + header: headerText + }, + global: { + plugins: [createTestingPinia({ createSpy: vi.fn })] + } + }) + + expect(wrapper.text()).toContain(headerText) + }) + + it('Test row highlighting', async () => { + const testStore = createTestingPinia({ createSpy: vi.fn }) + store().state.submissionIdsToComparisonFileName.set('A', new Map([['B', 'file1']])) + store().state.submissionIdsToComparisonFileName.set('B', new Map([['A', 'file1']])) + store().state.submissionIdsToComparisonFileName.set('C', new Map([['D', 'file2']])) + store().state.submissionIdsToComparisonFileName.set('D', new Map([['C', 'file2']])) + const wrapper = mount(ComparisonTable, { + props: { + topComparisons: [ + { + sortingPlace: 0, + id: 1, + firstSubmissionId: 'A', + secondSubmissionId: 'B', + similarities: { + [MetricType.AVERAGE]: 1, + [MetricType.MAXIMUM]: 1 + }, + clusterIndex: -1 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'C', + secondSubmissionId: 'D', + similarities: { + [MetricType.AVERAGE]: 1, + [MetricType.MAXIMUM]: 1 + }, + clusterIndex: -1 + } + ], + clusters: [] + }, + global: { + plugins: [testStore, router] + } + }) + + const rows = wrapper.findAll('div.tableRow') + console.log(wrapper.text()) + expect(rows[0].classes()).not.toContain('!bg-accent') + expect(rows[1].classes()).not.toContain('!bg-accent') + + wrapper.setProps({ + highlightedRowIds: { firstID: 'A', secondID: 'B' } + }) + await flushPromises() + expect(rows[0].classes()).toContain('!bg-accent') + expect(rows[1].classes()).not.toContain('!bg-accent') + + wrapper.setProps({ + highlightedRowIds: { firstID: 'C', secondID: 'D' } + }) + await flushPromises() + expect(rows[0].classes()).not.toContain('!bg-accent') + expect(rows[1].classes()).toContain('!bg-accent') + + wrapper.setProps({ + highlightedRowIds: undefined + }) + await flushPromises() + expect(rows[0].classes()).not.toContain('!bg-accent') + expect(rows[1].classes()).not.toContain('!bg-accent') + }) +}) From d4f17350df304e8baa785dd2b604051a37a223a2 Mon Sep 17 00:00:00 2001 From: Alexander Vogt Date: Sun, 1 Sep 2024 11:14:31 +0200 Subject: [PATCH 4/5] test comparison table search and filter --- .../comparisonTable/ComparisonTable.test.ts | 342 ++++++++++++++++-- 1 file changed, 302 insertions(+), 40 deletions(-) diff --git a/report-viewer/tests/unit/components/comparisonTable/ComparisonTable.test.ts b/report-viewer/tests/unit/components/comparisonTable/ComparisonTable.test.ts index 530ee6341..2866e79a4 100644 --- a/report-viewer/tests/unit/components/comparisonTable/ComparisonTable.test.ts +++ b/report-viewer/tests/unit/components/comparisonTable/ComparisonTable.test.ts @@ -5,48 +5,144 @@ import { createTestingPinia } from '@pinia/testing' import { store } from '@/stores/store' import { MetricType } from '@/model/MetricType.ts' import { router } from '@/router' +import OptionsSelector from '@/components/optionsSelectors/OptionsSelectorComponent.vue' +import OptionComponent from '@/components/optionsSelectors/OptionComponent.vue' describe('ComparisonTable', async () => { it('Test search string filtering', async () => { const wrapper = mount(ComparisonTable, { props: { - topComparisons: [], + topComparisons: [ + { + sortingPlace: 0, + id: 1, + firstSubmissionId: 'A', + secondSubmissionId: 'B', + similarities: { + [MetricType.AVERAGE]: 1, + [MetricType.MAXIMUM]: 0.5 + }, + clusterIndex: -1 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'C', + secondSubmissionId: 'D', + similarities: { + [MetricType.AVERAGE]: 0.5, + [MetricType.MAXIMUM]: 1 + }, + clusterIndex: -1 + } + ], clusters: [] }, global: { - plugins: [createTestingPinia({ createSpy: vi.fn })] + plugins: [getStore(), router] } }) - const searchValue = 'JPlag' + // check that filtering works with one name + wrapper.find('input').setValue('A') + await flushPromises() + const displayedComparisonsSingleName = wrapper.vm.displayedComparisons + expect(displayedComparisonsSingleName.length).toBe(1) + expect(displayedComparisonsSingleName[0].firstSubmissionId).toBe('A') + expect(displayedComparisonsSingleName[0].secondSubmissionId).toBe('B') - wrapper.find('input').setValue(searchValue) + // check that filtering works with two names + wrapper.find('input').setValue('A D') await flushPromises() + const displayedComparisonsTwoNames = wrapper.vm.displayedComparisons + expect(displayedComparisonsTwoNames.length).toBe(2) }) - it('Test header prop', async () => { - const headerText = 'Custom Header' - + it('Test search bar filtering by index', async () => { const wrapper = mount(ComparisonTable, { props: { - topComparisons: [], - clusters: [], - header: headerText + topComparisons: [ + { + sortingPlace: 0, + id: 1, + firstSubmissionId: 'A', + secondSubmissionId: 'B', + similarities: { + [MetricType.AVERAGE]: 0.3, + [MetricType.MAXIMUM]: 0.5 + }, + clusterIndex: -1 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'C', + secondSubmissionId: 'D', + similarities: { + [MetricType.AVERAGE]: 0.5, + [MetricType.MAXIMUM]: 1 + }, + clusterIndex: -1 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'E', + secondSubmissionId: 'F', + similarities: { + [MetricType.AVERAGE]: 0.3, + [MetricType.MAXIMUM]: 0.1 + }, + clusterIndex: -1 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'H', + secondSubmissionId: 'G', + similarities: { + [MetricType.AVERAGE]: 0.9, + [MetricType.MAXIMUM]: 0.2 + }, + clusterIndex: -1 + } + ], + clusters: [] }, global: { - plugins: [createTestingPinia({ createSpy: vi.fn })] + plugins: [getStore(), router] } }) - expect(wrapper.text()).toContain(headerText) + wrapper.find('input').setValue('2') + await flushPromises() + const displayedComparisonsIndex1 = wrapper.vm.displayedComparisons + expect(displayedComparisonsIndex1.length).toBe(1) + expect(displayedComparisonsIndex1[0].firstSubmissionId).toBe('C') + + wrapper.find('input').setValue('2 3') + await flushPromises() + const displayedComparisonsIndex2 = wrapper.vm.displayedComparisons + expect(displayedComparisonsIndex2.length).toBe(2) + expect(displayedComparisonsIndex2[0].firstSubmissionId).toBe('C') + expect(displayedComparisonsIndex2[1].firstSubmissionId).toBe('A') + + wrapper.find('input').setValue('index:1') + await flushPromises() + const displayedComparisonsIndex3 = wrapper.vm.displayedComparisons + expect(displayedComparisonsIndex3.length).toBe(1) + expect(displayedComparisonsIndex3[0].firstSubmissionId).toBe('H') + + const metricOptions = wrapper.getComponent(OptionsSelector).findAllComponents(OptionComponent) + await metricOptions[1].trigger('click') + wrapper.find('input').setValue('index:2') + await flushPromises() + const displayedComparisonsIndex4 = wrapper.vm.displayedComparisons + expect(displayedComparisonsIndex4.length).toBe(1) + expect(displayedComparisonsIndex4[0].firstSubmissionId).toBe('A') }) - it('Test row highlighting', async () => { - const testStore = createTestingPinia({ createSpy: vi.fn }) - store().state.submissionIdsToComparisonFileName.set('A', new Map([['B', 'file1']])) - store().state.submissionIdsToComparisonFileName.set('B', new Map([['A', 'file1']])) - store().state.submissionIdsToComparisonFileName.set('C', new Map([['D', 'file2']])) - store().state.submissionIdsToComparisonFileName.set('D', new Map([['C', 'file2']])) + it('Test search bar filtering by metric', async () => { const wrapper = mount(ComparisonTable, { props: { topComparisons: [ @@ -56,8 +152,8 @@ describe('ComparisonTable', async () => { firstSubmissionId: 'A', secondSubmissionId: 'B', similarities: { - [MetricType.AVERAGE]: 1, - [MetricType.MAXIMUM]: 1 + [MetricType.AVERAGE]: 0.3, + [MetricType.MAXIMUM]: 0.5 }, clusterIndex: -1 }, @@ -67,43 +163,209 @@ describe('ComparisonTable', async () => { firstSubmissionId: 'C', secondSubmissionId: 'D', similarities: { - [MetricType.AVERAGE]: 1, + [MetricType.AVERAGE]: 0.4, [MetricType.MAXIMUM]: 1 }, clusterIndex: -1 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'E', + secondSubmissionId: 'F', + similarities: { + [MetricType.AVERAGE]: 0.3, + [MetricType.MAXIMUM]: 0.1 + }, + clusterIndex: -1 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'H', + secondSubmissionId: 'G', + similarities: { + [MetricType.AVERAGE]: 0.9, + [MetricType.MAXIMUM]: 0.2 + }, + clusterIndex: -1 } ], clusters: [] }, global: { - plugins: [testStore, router] + plugins: [getStore(), router] } }) - const rows = wrapper.findAll('div.tableRow') - console.log(wrapper.text()) - expect(rows[0].classes()).not.toContain('!bg-accent') - expect(rows[1].classes()).not.toContain('!bg-accent') + // check that filtering works over all metrics when no metric is specified + wrapper.find('input').setValue('>45') + await flushPromises() + const displayedComparisonsMetricNoPercentage = wrapper.vm.displayedComparisons + expect(displayedComparisonsMetricNoPercentage.length).toBe(3) - wrapper.setProps({ - highlightedRowIds: { firstID: 'A', secondID: 'B' } - }) + // check that filtering works with and without percentage + wrapper.find('input').setValue('>45%') await flushPromises() - expect(rows[0].classes()).toContain('!bg-accent') - expect(rows[1].classes()).not.toContain('!bg-accent') + const displayedComparisonsMetricWithPercentage = wrapper.vm.displayedComparisons + expect(displayedComparisonsMetricWithPercentage.length).toBe(3) + expect(displayedComparisonsMetricWithPercentage).toEqual(displayedComparisonsMetricNoPercentage) - wrapper.setProps({ - highlightedRowIds: { firstID: 'C', secondID: 'D' } - }) + // check that filtering works on max metric percentage + wrapper.find('input').setValue('max:>45') + await flushPromises() + expect(wrapper.vm.displayedComparisons.length).toBe(2) + + // check that filtering works on average metric percentage + wrapper.find('input').setValue('avg:>45') + await flushPromises() + expect(wrapper.vm.displayedComparisons.length).toBe(1) + + // check that filtering works correctly on greater, greater or equal, less and less or equal + wrapper.find('input').setValue('max:>50') + await flushPromises() + expect(wrapper.vm.displayedComparisons.length).toBe(1) + + wrapper.find('input').setValue('max:>=50') await flushPromises() - expect(rows[0].classes()).not.toContain('!bg-accent') - expect(rows[1].classes()).toContain('!bg-accent') + expect(wrapper.vm.displayedComparisons.length).toBe(2) - wrapper.setProps({ - highlightedRowIds: undefined + wrapper.find('input').setValue('max:<50%') + await flushPromises() + expect(wrapper.vm.displayedComparisons.length).toBe(2) + + wrapper.find('input').setValue('max:<=50') + await flushPromises() + expect(wrapper.vm.displayedComparisons.length).toBe(3) + }) + + it('Test sorting working', async () => { + const wrapper = mount(ComparisonTable, { + props: { + topComparisons: [ + { + sortingPlace: 0, + id: 1, + firstSubmissionId: 'A', + secondSubmissionId: 'B', + similarities: { + [MetricType.AVERAGE]: 0.3, + [MetricType.MAXIMUM]: 0.5 + }, + clusterIndex: 0 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'C', + secondSubmissionId: 'D', + similarities: { + [MetricType.AVERAGE]: 0.5, + [MetricType.MAXIMUM]: 1 + }, + clusterIndex: 1 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'E', + secondSubmissionId: 'F', + similarities: { + [MetricType.AVERAGE]: 0.3, + [MetricType.MAXIMUM]: 0.1 + }, + clusterIndex: 2 + }, + { + sortingPlace: 1, + id: 2, + firstSubmissionId: 'H', + secondSubmissionId: 'G', + similarities: { + [MetricType.AVERAGE]: 0.9, + [MetricType.MAXIMUM]: 0.2 + }, + clusterIndex: -1 + } + ], + clusters: [ + { + averageSimilarity: 0.5, + strength: 0.5, + members: ['A', 'B'] + }, + { + averageSimilarity: 0.6, + strength: 0.5, + members: ['C', 'D'] + }, + { + averageSimilarity: 0.9, + strength: 0.5, + members: ['E', 'F'] + } + ] + }, + global: { + plugins: [getStore(), router] + } }) + + // Test sorting by average + const displayedComparisonsAverageSorted = wrapper.vm.displayedComparisons + expect(displayedComparisonsAverageSorted[0].firstSubmissionId).toBe('H') + expect(displayedComparisonsAverageSorted[1].firstSubmissionId).toBe('C') + expect(displayedComparisonsAverageSorted[2].firstSubmissionId).toBe('A') + expect(displayedComparisonsAverageSorted[3].firstSubmissionId).toBe('E') + + const metricOptions = wrapper.getComponent(OptionsSelector).findAllComponents(OptionComponent) + await metricOptions[1].trigger('click') await flushPromises() - expect(rows[0].classes()).not.toContain('!bg-accent') - expect(rows[1].classes()).not.toContain('!bg-accent') + + // Test sorting by max + const displayedComparisonsMaxSorted = wrapper.vm.displayedComparisons + expect(displayedComparisonsMaxSorted[0].firstSubmissionId).toBe('C') + expect(displayedComparisonsMaxSorted[1].firstSubmissionId).toBe('A') + expect(displayedComparisonsMaxSorted[2].firstSubmissionId).toBe('H') + expect(displayedComparisonsMaxSorted[3].firstSubmissionId).toBe('E') + + await metricOptions[2].trigger('click') + await flushPromises() + + // Test sorting by cluster + const displayedComparisonsClusterSorted = wrapper.vm.displayedComparisons + expect(displayedComparisonsClusterSorted[0].firstSubmissionId).toBe('E') + expect(displayedComparisonsClusterSorted[1].firstSubmissionId).toBe('C') + expect(displayedComparisonsClusterSorted[2].firstSubmissionId).toBe('A') + }) + + it('Test header prop', async () => { + const headerText = 'Custom Header' + + const wrapper = mount(ComparisonTable, { + props: { + topComparisons: [], + clusters: [], + header: headerText + }, + global: { + plugins: [createTestingPinia({ createSpy: vi.fn }), router] + } + }) + + expect(wrapper.text()).toContain(headerText) }) }) + +function getStore() { + const testStore = createTestingPinia({ createSpy: vi.fn }) + store().state.submissionIdsToComparisonFileName.set('A', new Map([['B', 'file1']])) + store().state.submissionIdsToComparisonFileName.set('B', new Map([['A', 'file1']])) + store().state.submissionIdsToComparisonFileName.set('C', new Map([['D', 'file2']])) + store().state.submissionIdsToComparisonFileName.set('D', new Map([['C', 'file2']])) + store().state.submissionIdsToComparisonFileName.set('E', new Map([['F', 'file3']])) + store().state.submissionIdsToComparisonFileName.set('F', new Map([['E', 'file3']])) + store().state.submissionIdsToComparisonFileName.set('G', new Map([['H', 'file4']])) + store().state.submissionIdsToComparisonFileName.set('H', new Map([['G', 'file4']])) + return testStore +} From 487f0c9fbbed365287afc7427172c84d206dcaed Mon Sep 17 00:00:00 2001 From: Alexander Vogt Date: Sun, 1 Sep 2024 12:06:05 +0200 Subject: [PATCH 5/5] remove skipped test --- .../components/comparisonTable/ComparisonTableFilter.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts b/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts index 24c06c240..2b11ea6a5 100644 --- a/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts +++ b/report-viewer/tests/unit/components/comparisonTable/ComparisonTableFilter.test.ts @@ -149,8 +149,7 @@ describe('ComparisonTableFilter', async () => { expect(store().state.anonymous).not.toContain('A') }) - // skipped, because of #1946 - it.skip('Test deanoymization - name with spaces', async () => { + it('Test deanoymization - name with spaces', async () => { const wrapper = mount(ComparisonTableFilter, { props: { searchString: '',