From b18da959dbf4a8727e55f744196ec678d6d70476 Mon Sep 17 00:00:00 2001 From: mikiher Date: Sun, 23 Mar 2025 20:40:58 +0200 Subject: [PATCH 1/3] Fix broken component tests --- client/cypress/tests/components/cards/AuthorCard.cy.js | 8 +++++--- client/cypress/tests/components/cards/LazyBookCard.cy.js | 3 ++- .../cypress/tests/components/cards/LazySeriesCard.cy.js | 8 ++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/client/cypress/tests/components/cards/AuthorCard.cy.js b/client/cypress/tests/components/cards/AuthorCard.cy.js index 21c638e18..4c4a1cb88 100644 --- a/client/cypress/tests/components/cards/AuthorCard.cy.js +++ b/client/cypress/tests/components/cards/AuthorCard.cy.js @@ -19,7 +19,9 @@ describe('AuthorCard', () => { const mocks = { $strings: { LabelBooks: 'Books', - ButtonQuickMatch: 'Quick Match' + ButtonQuickMatch: 'Quick Match', + ToastAuthorUpdateSuccess: 'Author updated', + ToastAuthorUpdateSuccessNoImageFound: 'Author updated (no image found)' }, $store: { getters: { @@ -167,7 +169,7 @@ describe('AuthorCard', () => { cy.get('&match').click() cy.get('&spinner').should('be.hidden') - cy.get('@success').should('have.been.calledOnceWithExactly', 'Author John Doe was updated (no image found)') + cy.get('@success').should('have.been.calledOnceWithExactly', 'Author updated (no image found)') cy.get('@error').should('not.have.been.called') cy.get('@info').should('not.have.been.called') }) @@ -189,7 +191,7 @@ describe('AuthorCard', () => { cy.get('&match').click() cy.get('&spinner').should('be.hidden') - cy.get('@success').should('have.been.calledOnceWithExactly', 'Author John Doe was updated') + cy.get('@success').should('have.been.calledOnceWithExactly', 'Author updated') cy.get('@error').should('not.have.been.called') cy.get('@info').should('not.have.been.called') }) diff --git a/client/cypress/tests/components/cards/LazyBookCard.cy.js b/client/cypress/tests/components/cards/LazyBookCard.cy.js index c39c03023..dbe33f81d 100644 --- a/client/cypress/tests/components/cards/LazyBookCard.cy.js +++ b/client/cypress/tests/components/cards/LazyBookCard.cy.js @@ -172,6 +172,7 @@ describe('LazyBookCard', () => { }) it('shows titleImageNotReady and sets opacity 0 on coverImage when image not ready', () => { + mountOptions.mocks.$store.getters['globals/getLibraryItemCoverSrc'] = () => 'https://my.server.com/notfound.jpg' cy.mount(LazyBookCard, mountOptions) cy.get('&titleImageNotReady').should('be.visible') @@ -257,7 +258,7 @@ describe('LazyBookCard', () => { cy.get('#book-card-0').trigger('mouseover') cy.get('&titleImageNotReady').should('be.hidden') - cy.get('&seriesNameOverlay').should('be.visible').and('have.text', 'Middle Earth Chronicles') + cy.get('&seriesNameOverlay').should('be.visible').and('have.text', 'The Lord of the Rings') }) it('shows the seriesSequenceList when collapsed series has a sequence list', () => { diff --git a/client/cypress/tests/components/cards/LazySeriesCard.cy.js b/client/cypress/tests/components/cards/LazySeriesCard.cy.js index c637c604e..346259d27 100644 --- a/client/cypress/tests/components/cards/LazySeriesCard.cy.js +++ b/client/cypress/tests/components/cards/LazySeriesCard.cy.js @@ -30,6 +30,14 @@ describe('LazySeriesCard', () => { } const mocks = { + $getString: (id, args) => { + switch (id) { + case 'LabelAddedDate': + return `Added ${args[0]}` + default: + return null + } + }, $store: { getters: { 'user/getUserCanUpdate': true, From 59e099f9500b838731d4ea41d3c65320f22a332f Mon Sep 17 00:00:00 2001 From: mikiher Date: Mon, 24 Mar 2025 07:42:26 +0200 Subject: [PATCH 2/3] Add GitHub Actions workflow for running component tests --- .github/workflows/component-tests.yml | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/component-tests.yml diff --git a/.github/workflows/component-tests.yml b/.github/workflows/component-tests.yml new file mode 100644 index 000000000..fcc2c2138 --- /dev/null +++ b/.github/workflows/component-tests.yml @@ -0,0 +1,48 @@ +name: Run Component Tests + +on: + workflow_dispatch: + inputs: + ref: + description: 'Branch/Tag/SHA to test' + required: true + pull_request: + paths: + - 'client/**' + - '.github/workflows/component-tests.yml' + push: + paths: + - 'client/**' + - '.github/workflows/component-tests.yml' + +jobs: + run-component-tests: + name: Run Component Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout (push/pull request) + uses: actions/checkout@v4 + if: github.event_name != 'workflow_dispatch' + + - name: Checkout (workflow_dispatch) + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + if: github.event_name == 'workflow_dispatch' + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + + - name: Install dependencies + run: | + cd client + npm ci + + - name: Run tests + run: | + cd client + npm test From 718433183bea533b94e1744ffa45c3c8246beab7 Mon Sep 17 00:00:00 2001 From: advplyr Date: Thu, 27 Mar 2025 17:37:25 -0500 Subject: [PATCH 3/3] Fix Cover modal showing error image for items with no cover, update placeholder cover url to all come from global store --- client/components/cards/LazyBookCard.vue | 3 +-- client/components/covers/BookCover.vue | 4 ++-- client/components/covers/PreviewCover.vue | 7 ++++--- client/components/modals/item/tabs/Cover.vue | 8 +++++++- client/components/modals/player/QueueItemRow.vue | 2 +- client/pages/share/_slug.vue | 2 +- client/store/globals.js | 11 ++++++----- 7 files changed, 22 insertions(+), 15 deletions(-) diff --git a/client/components/cards/LazyBookCard.vue b/client/components/cards/LazyBookCard.vue index e9ad8c8de..35c959fad 100644 --- a/client/components/cards/LazyBookCard.vue +++ b/client/components/cards/LazyBookCard.vue @@ -223,8 +223,7 @@ export default { return this.mediaMetadata.explicit || false }, placeholderUrl() { - const config = this.$config || this.$nuxt.$config - return `${config.routerBasePath}/book_placeholder.jpg` + return this.store.getters['globals/getPlaceholderCoverSrc'] }, bookCoverSrc() { return this.store.getters['globals/getLibraryItemCoverSrc'](this._libraryItem, this.placeholderUrl) diff --git a/client/components/covers/BookCover.vue b/client/components/covers/BookCover.vue index 0a9248d22..e55d38c17 100644 --- a/client/components/covers/BookCover.vue +++ b/client/components/covers/BookCover.vue @@ -96,8 +96,8 @@ export default { return this.author }, placeholderUrl() { - const config = this.$config || this.$nuxt.$config - return `${config.routerBasePath}/book_placeholder.jpg` + const store = this.$store || this.$nuxt.$store + return store.getters['globals/getPlaceholderCoverSrc'] }, fullCoverUrl() { if (!this.libraryItem) return null diff --git a/client/components/covers/PreviewCover.vue b/client/components/covers/PreviewCover.vue index 0b73b009c..0ce7e55ed 100644 --- a/client/components/covers/PreviewCover.vue +++ b/client/components/covers/PreviewCover.vue @@ -18,7 +18,7 @@ -

{{ resolution }}

+

{{ resolution }}

@@ -65,11 +65,12 @@ export default { return 0.8 * this.sizeMultiplier }, resolution() { + if (!this.naturalWidth || !this.naturalHeight) return null return `${this.naturalWidth}×${this.naturalHeight}px` }, placeholderUrl() { - const config = this.$config || this.$nuxt.$config - return `${config.routerBasePath}/book_placeholder.jpg` + const store = this.$store || this.$nuxt.$store + return store.getters['globals/getPlaceholderCoverSrc'] } }, methods: { diff --git a/client/components/modals/item/tabs/Cover.vue b/client/components/modals/item/tabs/Cover.vue index fd258760a..17979f708 100644 --- a/client/components/modals/item/tabs/Cover.vue +++ b/client/components/modals/item/tabs/Cover.vue @@ -2,7 +2,7 @@
- +
@@ -157,6 +157,12 @@ export default { coverPath() { return this.media.coverPath }, + coverUrl() { + if (!this.coverPath) { + return this.$store.getters['globals/getPlaceholderCoverSrc'] + } + return this.$store.getters['globals/getLibraryItemCoverSrcById'](this.libraryItemId, this.libraryItemUpdatedAt, true) + }, mediaMetadata() { return this.media.metadata || {} }, diff --git a/client/components/modals/player/QueueItemRow.vue b/client/components/modals/player/QueueItemRow.vue index 2eb1bc3b6..9ac01a167 100644 --- a/client/components/modals/player/QueueItemRow.vue +++ b/client/components/modals/player/QueueItemRow.vue @@ -55,7 +55,7 @@ export default { return this.item.coverPath }, coverUrl() { - if (!this.coverPath) return `${this.$config.routerBasePath}/book_placeholder.jpg` + if (!this.coverPath) return this.$store.getters['globals/getPlaceholderCoverSrc'] return this.$store.getters['globals/getLibraryItemCoverSrcById'](this.libraryItemId) }, bookCoverAspectRatio() { diff --git a/client/pages/share/_slug.vue b/client/pages/share/_slug.vue index e7d00f005..bcc779d96 100644 --- a/client/pages/share/_slug.vue +++ b/client/pages/share/_slug.vue @@ -64,7 +64,7 @@ export default { return this.mediaItemShare.playbackSession }, coverUrl() { - if (!this.playbackSession.coverPath) return `${this.$config.routerBasePath}/book_placeholder.jpg` + if (!this.playbackSession.coverPath) return this.$store.getters['globals/getPlaceholderCoverSrc'] return `${this.$config.routerBasePath}/public/share/${this.mediaItemShare.slug}/cover` }, downloadUrl() { diff --git a/client/store/globals.js b/client/store/globals.js index 65878fb44..7b416196a 100644 --- a/client/store/globals.js +++ b/client/store/globals.js @@ -87,7 +87,7 @@ export const getters = { getLibraryItemCoverSrc: (state, getters, rootState, rootGetters) => (libraryItem, placeholder = null, raw = false) => { - if (!placeholder) placeholder = `${rootState.routerBasePath}/book_placeholder.jpg` + if (!placeholder) placeholder = getters.getPlaceholderCoverSrc if (!libraryItem) return placeholder const media = libraryItem.media if (!media?.coverPath || media.coverPath === placeholder) return placeholder @@ -95,7 +95,6 @@ export const getters = { // Absolute URL covers (should no longer be used) if (media.coverPath.startsWith('http:') || media.coverPath.startsWith('https:')) return media.coverPath - const userToken = rootGetters['user/getToken'] const lastUpdate = libraryItem.updatedAt || Date.now() const libraryItemId = libraryItem.libraryItemId || libraryItem.id // Workaround for /users/:id page showing media progress covers return `${rootState.routerBasePath}/api/items/${libraryItemId}/cover?ts=${lastUpdate}${raw ? '&raw=1' : ''}` @@ -103,11 +102,13 @@ export const getters = { getLibraryItemCoverSrcById: (state, getters, rootState, rootGetters) => (libraryItemId, timestamp = null, raw = false) => { - const placeholder = `${rootState.routerBasePath}/book_placeholder.jpg` - if (!libraryItemId) return placeholder - const userToken = rootGetters['user/getToken'] + if (!libraryItemId) return getters.getPlaceholderCoverSrc + return `${rootState.routerBasePath}/api/items/${libraryItemId}/cover?${raw ? '&raw=1' : ''}${timestamp ? `&ts=${timestamp}` : ''}` }, + getPlaceholderCoverSrc: (state, getters, rootState, rootGetters) => { + return `${rootState.routerBasePath}/book_placeholder.jpg` + }, getIsBatchSelectingMediaItems: (state) => { return state.selectedMediaItems.length }