diff --git a/resource/js/vocab-search.js b/resource/js/vocab-search.js index 1678cef7..9c46f21c 100644 --- a/resource/js/vocab-search.js +++ b/resource/js/vocab-search.js @@ -16,7 +16,7 @@ const vocabSearch = Vue.createApp({ }, mounted () { this.languages = window.SKOSMOS.languageOrder - this.selectedLanguage = window.SKOSMOS.content_lang + this.selectedLanguage = this.parseSearchLang() this.searchCounter = 0 this.languageStrings = window.SKOSMOS.language_strings[window.SKOSMOS.lang] ?? window.SKOSMOS.language_strings.en this.msgs = window.SKOSMOS.msgs[window.SKOSMOS.lang] ?? window.SKOSMOS.msgs.en @@ -45,7 +45,8 @@ const vocabSearch = Vue.createApp({ const mySearchCounter = this.searchCounter + 1 // make sure we can identify this search later in case of several ongoing searches this.searchCounter = mySearchCounter let skosmosSearchUrl = 'rest/v1/' + window.SKOSMOS.vocab + '/search?' - const skosmosSearchUrlParams = new URLSearchParams({ query: this.formatSearchTerm(), lang: window.SKOSMOS.lang, unique: true }) + const skosmosSearchUrlParams = new URLSearchParams({ query: this.formatSearchTerm(), unique: true }) + if (this.selectedLanguage !== 'all') skosmosSearchUrlParams.set('lang', this.selectedLanguage) skosmosSearchUrl += skosmosSearchUrlParams.toString() fetch(skosmosSearchUrl) @@ -67,8 +68,39 @@ const vocabSearch = Vue.createApp({ } return false }, - renderMatchingPart (searchTerm, label) { + parseSearchLang () { + // if content language can be found from uri params, use that and update it to SKOSMOS object and to search lang cookie + const urlParams = new URLSearchParams(window.location.search) + const paramLang = urlParams.get('clang') + const anyLang = urlParams.get('anylang') + if (anyLang) { + this.changeLang('all') + return 'all' + } + if (paramLang) { + this.changeLang(paramLang) + return paramLang + } + // use searchLangCookie if it can be found, otherwise pick content lang from SKOSMOS object + const cookies = document.cookie.split('; ') + const searchLangCookie = cookies.find(cookie => + cookie.startsWith('SKOSMOS_SEARCH_LANG=')) + if (searchLangCookie) { + const selectedLanguage = searchLangCookie.split('=')[1] + if (selectedLanguage !== 'all') { + window.SKOSMOS.content_lang = selectedLanguage + } + return selectedLanguage + } else { + return window.SKOSMOS.content_lang + } + }, + renderMatchingPart (searchTerm, label, lang = null) { if (label) { + let langSpec = '' + if (lang && this.selectedLanguage === 'all') { + langSpec = ' (' + lang + ')' + } const searchTermLowerCase = searchTerm.toLowerCase() const labelLowerCase = label.toLowerCase() if (labelLowerCase.includes(searchTermLowerCase)) { @@ -77,10 +109,10 @@ const vocabSearch = Vue.createApp({ return { before: label.substring(0, startIndex), match: label.substring(startIndex, endIndex), - after: label.substring(endIndex) + after: label.substring(endIndex) + langSpec } } - return label + return label + langSpec } return null }, @@ -95,21 +127,25 @@ const vocabSearch = Vue.createApp({ renderResults () { // TODO: get the results list form cache if it is implemented const renderedSearchTerm = this.searchTerm // save the search term in case it changes while rendering + this.renderedResultsList.forEach(result => { if ('hiddenLabel' in result) { result.hitType = 'hidden' - result.hit = this.renderMatchingPart(renderedSearchTerm, result.prefLabel) + result.hit = this.renderMatchingPart(renderedSearchTerm, result.prefLabel, result.lang) } else if ('altLabel' in result) { result.hitType = 'alt' - result.hit = this.renderMatchingPart(renderedSearchTerm, result.altLabel) + result.hit = this.renderMatchingPart(renderedSearchTerm, result.altLabel, result.lang) result.hitPref = this.renderMatchingPart(renderedSearchTerm, result.prefLabel) } else { if (this.notationMatches(renderedSearchTerm, result.notation)) { result.hitType = 'notation' - result.hit = this.renderMatchingPart(renderedSearchTerm, result.notation) + result.hit = this.renderMatchingPart(renderedSearchTerm, result.notation, result.lang) + } else if ('matchedPrefLabel' in result) { + result.hitType = 'pref' + result.hit = this.renderMatchingPart(renderedSearchTerm, result.matchedPrefLabel, result.lang) } else if ('prefLabel' in result) { result.hitType = 'pref' - result.hit = this.renderMatchingPart(renderedSearchTerm, result.prefLabel) + result.hit = this.renderMatchingPart(renderedSearchTerm, result.prefLabel, result.lang) } } if ('uri' in result) { // create relative Skosmos page URL from the search result URI @@ -144,13 +180,26 @@ const vocabSearch = Vue.createApp({ const currentVocab = window.SKOSMOS.vocab + '/' + window.SKOSMOS.lang + '/' const vocabHref = window.location.href.substring(0, window.location.href.lastIndexOf(window.SKOSMOS.vocab)) + currentVocab const searchUrlParams = new URLSearchParams({ clang: window.SKOSMOS.content_lang, q: this.searchTerm }) - if (this.selectedLanguage === 'all') searchUrlParams.set('anylang', 'on') + if (this.selectedLanguage === 'all') searchUrlParams.set('anylang', 'true') const searchUrl = vocabHref + 'search?' + searchUrlParams.toString() window.location.href = searchUrl }, - changeLang () { - window.SKOSMOS.content_lang = this.selectedLanguage - // TODO: Implement (a normal) page load to change content according to the new content language + changeLang (lang) { + this.selectedLanguage = lang + this.setSearchLangCookie(lang) + this.resetSearchTermAndHideDropdown() + }, + changeContentLangAndReload (lang) { + this.changeLang(lang) + const params = new URLSearchParams(window.location.search) + if (lang === 'all') { + params.set('anylang', 'true') + } else { + params.delete('anylang') + params.set('clang', lang) + } + this.$forceUpdate() + window.location.search = params.toString() }, resetSearchTermAndHideDropdown () { this.searchTerm = '' @@ -163,6 +212,14 @@ const vocabSearch = Vue.createApp({ showAutoComplete () { this.showDropdown = true this.$forceUpdate() + }, + setSearchLangCookie (lang) { + // The cookie path should be relative if the baseHref is known + let cookiePath = '/' + if (window.SKOSMOS.baseHref && window.SKOSMOS.baseHref.replace(window.origin, '')) { + cookiePath = window.SKOSMOS.baseHref.replace(window.origin, '') + } + document.cookie = `SKOSMOS_SEARCH_LANG=${this.selectedLanguage};path=${cookiePath}` } }, template: ` @@ -170,7 +227,7 @@ const vocabSearch = Vue.createApp({
diff --git a/src/view/scripts.inc b/src/view/scripts.inc index 87a4d99d..be8641c9 100644 --- a/src/view/scripts.inc +++ b/src/view/scripts.inc @@ -24,6 +24,7 @@ window.SKOSMOS = { {%- if request.plugins.callbacks ~%} "pluginCallbacks": [{% for function in request.plugins.callbacks %}{% if not loop.first %}, {% endif %}"{{ function }}"{% endfor %}], {%- endif ~%} + "baseHref": "{{ BaseHref }}", "language_strings": { "fi": { "fi": "suomi", "en": "englanti", "se": "pohjoissaame", diff --git a/tests/cypress/template/vocab-search-bar.cy.js b/tests/cypress/template/vocab-search-bar.cy.js index b5f1ce81..9753465f 100644 --- a/tests/cypress/template/vocab-search-bar.cy.js +++ b/tests/cypress/template/vocab-search-bar.cy.js @@ -1,157 +1,199 @@ describe('Vocab search bar', () => { - it('search can be done with a chosen content language', () => { - // go to YSO vocab front page - cy.visit('/yso/fi/') - // Select an option from the dropdown - cy.get('#search-wrapper select').select('sv'); + describe('Search Language', () => { + it('search can be done with a chosen content language', () => { + // go to YSO vocab front page + cy.visit('/yso/fi/') - // Enter a search term - cy.get('#search-wrapper input').type('Katt'); + // Select an option from the dropdown + cy.get('#search-wrapper select').select('sv'); - // Click the search button - cy.get('#search-button').click(); + // Enter a search term + cy.get('#search-wrapper input').type('Katt'); - //Verify the search page url (search result page tests are elsewhere) - cy.url().should('include', 'q=Katt').and('include', 'clang=sv'); + // Click the search button + cy.get('#search-button').click(); - }) + // Verify the search page url (search result page tests are elsewhere) + cy.url().should('include', 'q=Katt').and('include', 'clang=sv'); + }) - it('search can be done with all languages', () => { - // go to YSO vocab front page - cy.visit('/yso/fi/') + it('search can be done with all languages', () => { + // go to YSO vocab front page + cy.visit('/yso/fi/') - // Choose 'all' languages - cy.get('#search-wrapper select').select('all'); + // Choose 'all' languages + cy.get('#search-wrapper select').select('all'); - // Enter a search term - cy.get('#search-wrapper input').type('Katt'); + // Enter a search term + cy.get('#search-wrapper input').type('Katt'); - // Click the search button - cy.get('#search-button').click(); + // Click the search button + cy.get('#search-button').click(); - //Verify the search page url (search result page tests are elsewhere) - cy.url().should('include', 'q=Katt').and('include', 'anylang=on'); - }) + // Verify the search page url (search result page tests are elsewhere) + cy.url().should('include', 'q=Katt').and('include', 'anylang=true'); + }) - it('Writing in the text field triggers the autocomplete results list', () => { - // go to YSO vocab front page - cy.visit('/yso/fi/') + it('search with all languages retains the previously chosen content language', () => { + // go to YSO vocab front page + cy.visit('/yso/fi/') - cy.get('#search-field').type('kas'); // perform autocomplete search - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible').children().should('have.length.greaterThan', 2); - }) + // Choose 'sv' for search & content language + cy.get('#search-wrapper select').select('sv'); - it('No results message is displayed if no results are found', () => { - // go to YSO vocab front page - cy.visit('/yso/en/') + // Choose 'all' for search language + cy.get('#search-wrapper select').select('all'); - cy.get('#search-field').type('kissa'); // even if the search yields no results, there shoulde a single line in the result list - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible').children().should('have.length.greaterThan', 0); - cy.get('#search-autocomplete-results').within(() => { - cy.get('li').eq(0).invoke('text').should('contain', 'No results') // the single result should display a no results message - }) - }) + // Verify the search page url has the previously chosen language as the content language + cy.url().should('include', 'clang=sv'); + }) - it('No results are displayed for autocomplete if there is not at leas two charecters in the search term', () => { - // go to YSO vocab front page - cy.visit('/yso/en/') + }); - cy.get('#search-field').type('k'); // even if the search yields no results, there shoulde a single line in the result list - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('not.be.visible'); - }) + describe('Autocomplete', () => { + it('Writing in the text field triggers the autocomplete results list', () => { + // go to YSO vocab front page + cy.visit('/yso/fi/') - it('The autocomplete list should not change due to previous searches completing', () => { - // go to YSO vocab front page - cy.visit('/yso/fi/') + cy.get('#search-field').type('kas'); // perform autocomplete search + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible').children().should('have.length.greaterThan', 2); + }) - cy.get('#search-field').type('ka'); - cy.wait(300); - cy.get('#search-field').type('i'); - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear - cy.get('#search-autocomplete-results').children().should('have.length', 1) - cy.wait(10000); // wait extra 10 seconds to see if the 'ka' search adds results to the list - cy.get('#search-autocomplete-results').children().should('have.length', 1) - }) + it('Special characters can be used in the search', () => { + // go to YSO vocab front page + cy.visit('/yso/fi/') - it('Clear button should hide the autocomplete list', () => { - // go to YSO vocab front page - cy.visit('/yso/en/') + cy.get('#search-field').type('*tus (*'); + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear - cy.get('#search-field').type('kas'); - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + cy.get('#search-autocomplete-results').within(() => { // the first result should have text ajoitus (historia) + cy.get('li').first().should('contain', 'ajoitus (historia)') + }) + }) - cy.get('#clear-button').click() - cy.get('#search-autocomplete-results').should('not.be.visible'); // the autocomplete should disappear - }) + it('No results message is displayed if no results are found', () => { + // go to YSO vocab front page + cy.visit('/yso/en/') - it('Emptying the text search field hides the autocomplete list', () => { - // go to YSO vocab front page - cy.visit('/yso/en/') + cy.get('#search-field').type('kissa'); // even if the search yields no results, there shoulde a single line in the result list + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible').children().should('have.length.greaterThan', 0); + cy.get('#search-autocomplete-results').within(() => { + cy.get('li').eq(0).invoke('text').should('contain', 'No results') // the single result should display a no results message + }) + }) - cy.get('#search-field').type('kis'); - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + it('No results are displayed for autocomplete if there is not at leas two charecters in the search term', () => { + // go to YSO vocab front page + cy.visit('/yso/en/') - cy.get('#search-field').clear(); - cy.get('#search-autocomplete-results').should('not.be.visible'); // the autocomplete should disappear - }) + cy.get('#search-field').type('k'); // even if the search yields no results, there shoulde a single line in the result list + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('not.be.visible'); + }) - it('Clicking outside of the autocomplete list hides the autocomplete list', () => { - // go to YSO vocab front page - cy.visit('/yso/en/') + it('The autocomplete list should not change due to previous searches completing', () => { + // go to YSO vocab front page + cy.visit('/yso/fi/') + + cy.get('#search-field').type('ka'); + cy.wait(300); + cy.get('#search-field').type('i'); + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + cy.get('#search-autocomplete-results').children().should('have.length', 1) + cy.wait(5000); // wait extra 5 seconds to see if the 'ka' search adds results to the list + cy.get('#search-autocomplete-results').children().should('have.length', 1) + }) - cy.get('#search-field').type('kas'); - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + it('Clear button should hide the autocomplete list', () => { + // go to YSO vocab front page + cy.visit('/yso/en/') - cy.get('#main-container').click({ force: true }); // using force true to click on elements not considered actionable - cy.get('#search-autocomplete-results').should('not.be.visible'); // the autocomplete should disappear - }) + cy.get('#search-field').type('kas'); + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear - it('AltLabel search results should bold the matching parts of altLabel', () => { - // go to YSO vocab front page - cy.visit('/yso/fi/') + cy.get('#clear-button').click() + cy.get('#search-autocomplete-results').should('not.be.visible'); // the autocomplete should disappear + }) - cy.get('#search-field').type('assyro'); - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + it('Emptying the text search field hides the autocomplete list', () => { + // go to YSO vocab front page + cy.visit('/yso/en/') - cy.get('#search-autocomplete-results').within(() => { // the first result should have matching part of text 'assyrologia' appearing in bold - cy.get('li').last().find('b').eq(0).should('have.text', 'assyro') + cy.get('#search-field').type('kis'); + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + + cy.get('#search-field').clear(); + cy.get('#search-autocomplete-results').should('not.be.visible'); // the autocomplete should disappear }) - }) - it('AltLabel search results should be displayed in italics', () => { - // go to YSO vocab front page - cy.visit('/yso/fi/') + it('Clicking outside of the autocomplete list hides the autocomplete list', () => { + // go to YSO vocab front page + cy.visit('/yso/en/') - cy.get('#search-field').type('assyro'); - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + cy.get('#search-field').type('kas'); + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear - cy.get('#search-autocomplete-results').within(() => { // the first result should have text 'assyrologia' appearing in italics - cy.get('li').last().find('i').eq(0).should('contain.text', 'assyrologia') + cy.get('#main-container').click({ force: true }); // using force true to click on elements not considered actionable + cy.get('#search-autocomplete-results').should('not.be.visible'); // the autocomplete should disappear }) - }) + }); - it('Notation search results should bold the matching parts of the notation', () => { - // go to YSO vocab front page - cy.visit('/yso/fi/') + describe('Search Result Rendering', () => { + it('AltLabel search results should bold the matching parts of altLabel', () => { + // go to YSO vocab front page + cy.visit('/yso/fi/') - cy.get('#search-field').type('51'); - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + cy.get('#search-field').type('assyro'); + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear - cy.get('#search-autocomplete-results').within(() => { // the first result should have text '51' appearing in bold - cy.get('li').last().find('b').eq(0).should('have.text', '51') + cy.get('#search-autocomplete-results').within(() => { // the first result should have matching part of text 'assyrologia' appearing in bold + cy.get('li').last().find('b').eq(0).should('have.text', 'assyro') + }) }) - }) - it('Special characters can be used in the search', () => { - // go to YSO vocab front page - cy.visit('/yso/fi/') + it('AltLabel search results should be displayed in italics', () => { + // go to YSO vocab front page + cy.visit('/yso/fi/') - cy.get('#search-field').type('*tus (*'); - cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + cy.get('#search-field').type('assyro'); + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear - cy.get('#search-autocomplete-results').within(() => { // the first result should have text ajoitus (historia) - cy.get('li').first().should('contain', 'ajoitus (historia)') + cy.get('#search-autocomplete-results').within(() => { // the first result should have text 'assyrologia' appearing in italics + cy.get('li').last().find('i').eq(0).should('contain.text', 'assyrologia') + }) }) - }) + + it('Notation search results should bold the matching parts of the notation', () => { + // go to YSO vocab front page + cy.visit('/yso/fi/') + + cy.get('#search-field').type('51'); + cy.get('#search-autocomplete-results', { timeout: 20000 }).should('be.visible'); // the autocomplete should appear + + cy.get('#search-autocomplete-results').within(() => { // the first result should have text '51' appearing in bold + cy.get('li').last().find('b').eq(0).should('have.text', '51') + }) + }) + }); + + describe('Cookie Management', () => { + it('The search language cookie is set', () => { + cy.visit('/yso/fi/'); + + // Select an option from the dropdown to set the search language + cy.get('#search-wrapper select').select('sv'); + + // Test that the cookie has been set correctly + cy.getCookie('SKOSMOS_SEARCH_LANG').should('have.property', 'value', 'sv'); + }); + + it('The search language cookie is read', () => { + cy.visit('/'); // setting the cookie at the front page to make sure the cookies are set globally + cy.setCookie('SKOSMOS_SEARCH_LANG', 'en'); + cy.visit('/yso/fi/'); + + // Test that the cookie value has been read and used to initialize the language selector + cy.get('#search-wrapper select').should('have.value', 'en'); + }); + }); })