mirror of
https://github.com/photoprism/photoprism.git
synced 2026-01-23 02:24:24 +00:00
Tests: write Initial Tests for .vue Components (#5003)
* Tests: Add initial Vue component tests * Tests: Update package scripts
This commit is contained in:
parent
b642be2c91
commit
7ab90720ff
7 changed files with 547 additions and 5 deletions
220
frontend/package-lock.json
generated
220
frontend/package-lock.json
generated
|
|
@ -22,10 +22,12 @@
|
|||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"@vitejs/plugin-vue": "^5.2.4",
|
||||
"@vitest/coverage-v8": "^3.1.3",
|
||||
"@vitest/ui": "^3.1.3",
|
||||
"@vue/compiler-sfc": "^3.5.13",
|
||||
"@vue/language-server": "^2.2.10",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vvo/tzdb": "^6.161.0",
|
||||
"axios": "^1.9.0",
|
||||
"axios-mock-adapter": "^2.1.0",
|
||||
|
|
@ -3968,6 +3970,12 @@
|
|||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@one-ini/wasm": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz",
|
||||
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@paralleldrive/cuid2": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz",
|
||||
|
|
@ -4911,6 +4919,19 @@
|
|||
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
|
||||
"integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.0.0 || ^6.0.0",
|
||||
"vue": "^3.2.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.3.tgz",
|
||||
|
|
@ -5377,6 +5398,16 @@
|
|||
"integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vue/test-utils": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz",
|
||||
"integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-beautify": "^1.14.9",
|
||||
"vue-component-type-helpers": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/typescript-plugin": {
|
||||
"version": "2.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@vue/typescript-plugin/-/typescript-plugin-2.2.10.tgz",
|
||||
|
|
@ -5609,6 +5640,15 @@
|
|||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/abbrev": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
|
||||
"integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||
|
|
@ -6949,6 +6989,22 @@
|
|||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/config-chain": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
|
||||
"integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ini": "^1.3.4",
|
||||
"proto-list": "~1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/config-chain/node_modules/ini": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/connect": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
|
||||
|
|
@ -8165,6 +8221,69 @@
|
|||
"pug": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/editorconfig": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz",
|
||||
"integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@one-ini/wasm": "0.1.1",
|
||||
"commander": "^10.0.0",
|
||||
"minimatch": "9.0.1",
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"bin": {
|
||||
"editorconfig": "bin/editorconfig"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/editorconfig/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/editorconfig/node_modules/commander": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/editorconfig/node_modules/minimatch": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
|
||||
"integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/editorconfig/node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
|
|
@ -11478,6 +11597,80 @@
|
|||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/js-beautify": {
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz",
|
||||
"integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"config-chain": "^1.1.13",
|
||||
"editorconfig": "^1.0.4",
|
||||
"glob": "^10.4.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"nopt": "^7.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"css-beautify": "js/bin/css-beautify.js",
|
||||
"html-beautify": "js/bin/html-beautify.js",
|
||||
"js-beautify": "js/bin/js-beautify.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/js-beautify/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-beautify/node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
"integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^3.1.2",
|
||||
"minimatch": "^9.0.4",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^1.11.1"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-beautify/node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-cookie": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
||||
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/js-stringify": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
|
||||
|
|
@ -12822,6 +13015,21 @@
|
|||
"node": ">=6.14.4"
|
||||
}
|
||||
},
|
||||
"node_modules/nopt": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
|
||||
"integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"abbrev": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"nopt": "bin/nopt.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
|
|
@ -15165,6 +15373,12 @@
|
|||
"asap": "~2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/proto-list": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||
"integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/protocol-buffers-schema": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
|
||||
|
|
@ -19026,6 +19240,12 @@
|
|||
"sanitize-html": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-component-type-helpers": {
|
||||
"version": "2.2.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.10.tgz",
|
||||
"integrity": "sha512-iDUO7uQK+Sab2tYuiP9D1oLujCWlhHELHMgV/cB13cuGbG4qwkLHvtfWb6FzvxrIOPDnU0oHsz2MlQjhYDeaHA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue-eslint-parser": {
|
||||
"version": "10.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.1.3.tgz",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"test:vitest": "vitest run",
|
||||
"test:vitest:watch": "vitest",
|
||||
"test:vitest:coverage": "vitest run --coverage",
|
||||
"test:vitest:component": "vitest run tests/vitest/component",
|
||||
"test:vitest:ui": "vitest --ui",
|
||||
"testcafe": "testcafe",
|
||||
"trace": "webpack --stats-children",
|
||||
|
|
@ -137,11 +138,13 @@
|
|||
"webpack-manifest-plugin": "^5.0.1",
|
||||
"webpack-md5-hash": "^0.0.6",
|
||||
"webpack-merge": "^6.0.1",
|
||||
"webpack-plugin-vuetify": "^3.1.1"
|
||||
"webpack-plugin-vuetify": "^3.1.1",
|
||||
"@vitejs/plugin-vue": "^5.2.4",
|
||||
"@vue/test-utils": "^2.4.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0",
|
||||
"npm": ">= 9.0.0",
|
||||
"yarn": "please use npm"
|
||||
}
|
||||
}
|
||||
}
|
||||
105
frontend/tests/vitest/component/loading-bar.test.js
Normal file
105
frontend/tests/vitest/component/loading-bar.test.js
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import PLoadingBar from "component/loading-bar.vue";
|
||||
|
||||
// Mock $event subscription
|
||||
const mockSubscribe = vi.fn();
|
||||
|
||||
// Mock queue function to execute callbacks immediately
|
||||
vi.mock("component/loading-bar.vue", async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
queue: (fn) => {
|
||||
fn((next) => {
|
||||
if (next) next();
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe("PLoadingBar component", () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
vi.useFakeTimers();
|
||||
|
||||
wrapper = mount(PLoadingBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$event: {
|
||||
subscribe: mockSubscribe,
|
||||
},
|
||||
},
|
||||
stubs: {
|
||||
transition: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("should render correctly", () => {
|
||||
expect(wrapper.vm).toBeTruthy();
|
||||
expect(wrapper.find("#p-loading-bar").exists()).toBe(true);
|
||||
expect(wrapper.find(".top-progress").exists()).toBe(false); // Initially not visible
|
||||
|
||||
// Check computed properties
|
||||
expect(wrapper.vm.progressColor).toBe("#29d"); // Default color
|
||||
expect(wrapper.vm.isStarted).toBe(false);
|
||||
});
|
||||
|
||||
it("should subscribe to ajax events on mount", () => {
|
||||
expect(mockSubscribe).toHaveBeenCalledTimes(2);
|
||||
expect(mockSubscribe.mock.calls[0][0]).toBe("ajax.start");
|
||||
expect(mockSubscribe.mock.calls[1][0]).toBe("ajax.end");
|
||||
});
|
||||
|
||||
it("should start the loading bar", async () => {
|
||||
wrapper.vm.start();
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
expect(wrapper.vm.visible).toBe(true);
|
||||
|
||||
// After transition, the bar should be displayed
|
||||
wrapper.vm.afterEnter();
|
||||
expect(wrapper.vm.status).not.toBeNull();
|
||||
});
|
||||
|
||||
it("should make progress visible when started", async () => {
|
||||
expect(wrapper.vm.visible).toBe(false);
|
||||
|
||||
// Start the bar
|
||||
wrapper.vm.start();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Should be visible now
|
||||
expect(wrapper.vm.visible).toBe(true);
|
||||
});
|
||||
|
||||
it("should handle error state", async () => {
|
||||
wrapper.vm.fail();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(wrapper.vm.error).toBe(true);
|
||||
expect(wrapper.vm.progressColor).toBe("#f44336"); // Error color
|
||||
});
|
||||
|
||||
it("should pause the loading bar", () => {
|
||||
wrapper.vm.start();
|
||||
wrapper.vm.pause();
|
||||
|
||||
expect(wrapper.vm.isPaused).toBe(true);
|
||||
});
|
||||
|
||||
it("should initialize progress to zero", () => {
|
||||
expect(wrapper.vm.progress).toBe(0);
|
||||
|
||||
expect(wrapper.vm.getProgress()).toBe(0);
|
||||
});
|
||||
});
|
||||
16
frontend/tests/vitest/component/loading.test.js
Normal file
16
frontend/tests/vitest/component/loading.test.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import PLoading from "component/loading.vue";
|
||||
|
||||
describe("PLoading component", () => {
|
||||
it("should render correctly", () => {
|
||||
const wrapper = mount(PLoading);
|
||||
|
||||
// Check if component renders
|
||||
expect(wrapper.vm).toBeTruthy();
|
||||
|
||||
// Check if the progress circular element exists
|
||||
const progressCircular = wrapper.find(".vprogresscircular-stub");
|
||||
expect(progressCircular.exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
125
frontend/tests/vitest/component/sidebar/info.test.js
Normal file
125
frontend/tests/vitest/component/sidebar/info.test.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import PSidebarInfo from "component/sidebar/info.vue";
|
||||
import { DateTime } from "luxon";
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock("component/map.vue", () => ({
|
||||
default: {
|
||||
name: "p-map",
|
||||
template: "<div class='p-map-stub'></div>",
|
||||
props: ["lat", "lng"],
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("options/formats", () => ({
|
||||
DATETIME_MED: "DATETIME_MED",
|
||||
DATETIME_MED_TZ: "DATETIME_MED_TZ",
|
||||
}));
|
||||
|
||||
describe("PSidebarInfo component", () => {
|
||||
let wrapper;
|
||||
const mockModel = {
|
||||
UID: "abc123",
|
||||
Title: "Test Title",
|
||||
Caption: "Test Caption",
|
||||
TakenAtLocal: "2023-01-01T10:00:00Z",
|
||||
TimeZone: "UTC",
|
||||
Lat: 52.52,
|
||||
Lng: 13.405,
|
||||
getTypeInfo: vi.fn().mockReturnValue("JPEG, 1920x1080"),
|
||||
getTypeIcon: vi.fn().mockReturnValue("mdi-file-image"),
|
||||
getLatLng: vi.fn().mockReturnValue("52.5200, 13.4050"),
|
||||
copyLatLng: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
wrapper = mount(PSidebarInfo, {
|
||||
props: {
|
||||
modelValue: mockModel,
|
||||
context: "photos",
|
||||
},
|
||||
global: {
|
||||
stubs: {
|
||||
PMap: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should render correctly with model data", () => {
|
||||
expect(wrapper.vm).toBeTruthy();
|
||||
expect(wrapper.find(".p-sidebar-info").exists()).toBe(true);
|
||||
|
||||
const html = wrapper.html();
|
||||
expect(html).toContain("Test Title");
|
||||
expect(html).toContain("Test Caption");
|
||||
|
||||
expect(mockModel.getTypeInfo).toHaveBeenCalled();
|
||||
expect(mockModel.getTypeIcon).toHaveBeenCalled();
|
||||
expect(mockModel.getLatLng).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should emit close event when close button is clicked", async () => {
|
||||
const closeButton = wrapper.find(".vbtn-stub");
|
||||
await closeButton.trigger("click");
|
||||
|
||||
expect(wrapper.emitted()).toHaveProperty("close");
|
||||
});
|
||||
|
||||
it("should trigger copyLatLng when location is clicked", async () => {
|
||||
// Find the location item by its class
|
||||
const clickableItems = wrapper.findAll(".clickable");
|
||||
if (clickableItems.length > 0) {
|
||||
await clickableItems[0].trigger("click");
|
||||
expect(mockModel.copyLatLng).toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
|
||||
it("should format time correctly", () => {
|
||||
// Mock DateTime.fromISO to return a controllable object
|
||||
const mockToLocaleString = vi.fn().mockReturnValue("January 1, 2023, 10:00 AM");
|
||||
DateTime.fromISO = vi.fn().mockReturnValue({
|
||||
toLocaleString: mockToLocaleString,
|
||||
});
|
||||
|
||||
const formattedTime = wrapper.vm.formatTime(mockModel);
|
||||
|
||||
expect(DateTime.fromISO).toHaveBeenCalledWith("2023-01-01T10:00:00Z", { zone: "UTC" });
|
||||
expect(mockToLocaleString).toHaveBeenCalledWith("DATETIME_MED_TZ");
|
||||
expect(formattedTime).toBe("January 1, 2023, 10:00 AM");
|
||||
});
|
||||
|
||||
it("should handle model with timezone", () => {
|
||||
// Create a model with non-UTC timezone
|
||||
const modelWithTZ = {
|
||||
...mockModel,
|
||||
TimeZone: "Europe/Berlin",
|
||||
};
|
||||
|
||||
// Mock DateTime.fromISO to return a controllable object
|
||||
const mockToLocaleString = vi.fn().mockReturnValue("January 1, 2023, 11:00 AM CET");
|
||||
DateTime.fromISO = vi.fn().mockReturnValue({
|
||||
toLocaleString: mockToLocaleString,
|
||||
});
|
||||
|
||||
const formattedTime = wrapper.vm.formatTime(modelWithTZ);
|
||||
|
||||
expect(DateTime.fromISO).toHaveBeenCalledWith("2023-01-01T10:00:00Z", { zone: "Europe/Berlin" });
|
||||
expect(mockToLocaleString).toHaveBeenCalledWith("DATETIME_MED_TZ");
|
||||
expect(formattedTime).toBe("January 1, 2023, 11:00 AM CET");
|
||||
});
|
||||
|
||||
it("should handle model without taken time", () => {
|
||||
const modelWithoutTime = {
|
||||
...mockModel,
|
||||
TakenAtLocal: null,
|
||||
};
|
||||
|
||||
const formattedTime = wrapper.vm.formatTime(modelWithoutTime);
|
||||
|
||||
expect(formattedTime).toBe("Unknown");
|
||||
});
|
||||
});
|
||||
72
frontend/tests/vitest/vue-setup.js
Normal file
72
frontend/tests/vitest/vue-setup.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { config } from "@vue/test-utils";
|
||||
import { vi } from "vitest";
|
||||
|
||||
// Mock Vuetify components
|
||||
const vuetifyComponents = [
|
||||
"VBtn",
|
||||
"VToolbar",
|
||||
"VToolbarTitle",
|
||||
"VList",
|
||||
"VListItem",
|
||||
"VDivider",
|
||||
"VProgressCircular",
|
||||
"VIcon",
|
||||
"VRow",
|
||||
"VCol",
|
||||
"VCard",
|
||||
"VCardTitle",
|
||||
"VCardText",
|
||||
"VCardActions",
|
||||
"VTextField",
|
||||
"VTextarea",
|
||||
"VSheet",
|
||||
];
|
||||
|
||||
// Create stubs for Vuetify components
|
||||
const vuetifyStubs = vuetifyComponents.reduce((acc, component) => {
|
||||
acc[component] = {
|
||||
name: component.toLowerCase(),
|
||||
template: `<div data-testid="${component.toLowerCase()}" class="${component.toLowerCase()}-stub"><slot></slot></div>`,
|
||||
};
|
||||
acc[component.toLowerCase()] = {
|
||||
name: component.toLowerCase(),
|
||||
template: `<div data-testid="${component.toLowerCase()}" class="${component.toLowerCase()}-stub"><slot></slot></div>`,
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Configure Vue Test Utils global configuration
|
||||
config.global.mocks = {
|
||||
$gettext: (text) => text,
|
||||
$isRtl: false,
|
||||
$config: {
|
||||
feature: (_name) => true,
|
||||
},
|
||||
};
|
||||
|
||||
config.global.stubs = {
|
||||
...vuetifyStubs,
|
||||
transition: false,
|
||||
};
|
||||
|
||||
config.global.directives = {
|
||||
tooltip: {
|
||||
mounted(el, binding) {
|
||||
el.setAttribute("data-tooltip", binding.value);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const originalMount = config.global.mount;
|
||||
config.global.mount = function (component, options = {}) {
|
||||
options.global = options.global || {};
|
||||
options.global.config = options.global.config || {};
|
||||
options.global.config.globalProperties = options.global.config.globalProperties || {};
|
||||
options.global.config.globalProperties.$emit = vi.fn();
|
||||
|
||||
return originalMount(component, options);
|
||||
};
|
||||
|
||||
export default {
|
||||
vuetifyStubs,
|
||||
};
|
||||
|
|
@ -1,18 +1,19 @@
|
|||
import { defineConfig } from "vitest/config";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), tsconfigPaths()],
|
||||
plugins: [react(), vue(), tsconfigPaths()],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "jsdom",
|
||||
setupFiles: "./tests/vitest/setup.js",
|
||||
setupFiles: ["./tests/vitest/setup.js", "./tests/vitest/vue-setup.js"],
|
||||
include: ["tests/vitest/**/*.{test,spec}.{js,jsx}"],
|
||||
coverage: {
|
||||
reporter: ["text", "html"],
|
||||
include: ["src/**/*.{js,jsx}"],
|
||||
include: ["src/**/*.{js,jsx,vue}"],
|
||||
exclude: ["src/locales/**"],
|
||||
},
|
||||
alias: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue