diff --git a/site/gatsby-site/cypress/e2e/integration/apps/checklistsForm.cy.js b/site/gatsby-site/cypress/e2e/integration/apps/checklistsForm.cy.js index eb5795dcb8..0a8cd189f4 100644 --- a/site/gatsby-site/cypress/e2e/integration/apps/checklistsForm.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/apps/checklistsForm.cy.js @@ -159,6 +159,31 @@ describe('Checklists App Form', () => { }); }); + maybeIt('Should trigger UI update on adding and removing tag', () => { + withLogin(({ user }) => { + interceptFindChecklist({ + ...defaultChecklist, + owner_id: user.userId, + }); + interceptUpsertChecklist({}); + + cy.visit(url); + + cy.get('#tags_methods_input').type('Transformer'); + cy.get('#tags_methods').contains('Transformer').click(); + + cy.waitForStableDOM(); + + cy.get('details').should('exist'); + + cy.get('.rbt-close').click(); + + cy.waitForStableDOM(); + + cy.get('details').should('not.exist'); + }); + }); + it('Should change sort order of risk items', () => { cy.viewport(1920, 1080); diff --git a/site/gatsby-site/cypress/e2e/integration/cite.cy.js b/site/gatsby-site/cypress/e2e/integration/cite.cy.js index cfef8e48c1..0e5a05dc25 100644 --- a/site/gatsby-site/cypress/e2e/integration/cite.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/cite.cy.js @@ -20,6 +20,8 @@ describe('Cite pages', () => { let user; + let lastIncidentId; + before('before', function () { // Skip all tests if the environment is empty since /cite/{incident_id} is not available Cypress.env('isEmptyEnvironment') && this.skip(); @@ -32,10 +34,14 @@ describe('Cite pages', () => { first_name last_name } + incidents(limit: 1, sortBy: INCIDENT_ID_DESC) { + incident_id + } } `, - }).then(({ data: { user: userData } }) => { + }).then(({ data: { user: userData, incidents: incidentsData } }) => { user = userData; + lastIncidentId = incidentsData[0].incident_id; }); }); @@ -323,7 +329,7 @@ describe('Cite pages', () => { cy.get(`.incident-ids-field [data-cy="token"]`).contains('10').should('be.visible'); }); - it('should render Next and Previous incident buttons', () => { + it('Should render Next and Previous incident buttons', () => { cy.visit(url); cy.contains('Next Incident').should('be.visible').should('have.attr', 'href', '/cite/11'); @@ -331,6 +337,30 @@ describe('Cite pages', () => { cy.contains('Previous Incident').should('be.visible').should('have.attr', 'href', '/cite/9'); }); + it('Should disable Previous and Next incident buttons on first and last incidents', () => { + cy.visit('/cite/1'); + + cy.contains('Previous Incident').within(($button) => { + cy.wrap($button).should('be.visible'); + cy.wrap($button).should('have.attr', 'disabled'); + cy.wrap($button).should('not.have.attr', 'href'); + }); + + cy.contains('Next Incident').should('be.visible').should('have.attr', 'href', '/cite/2'); + + cy.visit(`/cite/${lastIncidentId}`); + + cy.contains('Previous Incident') + .should('be.visible') + .should('have.attr', 'href', `/cite/${lastIncidentId - 1}`); + + cy.contains('Next Incident').within(($button) => { + cy.wrap($button).should('be.visible'); + cy.wrap($button).should('have.attr', 'disabled'); + cy.wrap($button).should('not.have.attr', 'href'); + }); + }); + maybeIt('Should show the edit incident form', () => { cy.login(Cypress.env('e2eUsername'), Cypress.env('e2ePassword')); diff --git a/site/gatsby-site/cypress/e2e/integration/socialShareButtons.cy.js b/site/gatsby-site/cypress/e2e/integration/socialShareButtons.cy.js index 815c784dff..3f960c0c42 100644 --- a/site/gatsby-site/cypress/e2e/integration/socialShareButtons.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/socialShareButtons.cy.js @@ -116,7 +116,14 @@ describe('Social Share buttons on pages', { retries: { runMode: 4 } }, () => { cy.get('[data-cy=btn-share-facebook]').first().click(); cy.get('@popup_facebook', { timeout: 8000 }).should('be.called'); - cy.url().should('contain', `https://www.facebook.com/sharer/sharer.php?u=${canonicalUrl}`); + // Take into consideration that the Facebook share button can open a new window with two different URLs depending on the environment + cy.url().should( + 'satisfy', + (url) => + (url.includes('https://www.facebook.com/login.php') && + url.includes('next=https%3A%2F%2Fwww.facebook.com%2Fsharer%2Fsharer.php')) || + url.includes(`https://www.facebook.com/sharer/sharer.php?u=${canonicalUrl}`) + ); }); }); }); diff --git a/site/gatsby-site/cypress/e2e/unit/functions/getUserAdminData.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/getUserAdminData.cy.js new file mode 100644 index 0000000000..1aec958d8e --- /dev/null +++ b/site/gatsby-site/cypress/e2e/unit/functions/getUserAdminData.cy.js @@ -0,0 +1,63 @@ +const getUserAdminData = require('../../../../../realm/functions/getUserAdminData'); + +describe('getUserAdminData', () => { + it('Should not return data if different user id and no permissions', () => { + global.context = { + // @ts-ignore + functions: { + execute: cy.stub().resolves({}), + }, + user: { + id: '2', + custom_data: { + roles: ['subscriber'], + }, + }, + }; + + cy.wrap(getUserAdminData({ userId: '1' })).then((response) => { + expect(global.context.functions.execute).to.not.called; + expect(response.email).to.be.null; + }); + }); + + it('Should return data if user id is the same as current user', () => { + global.context = { + // @ts-ignore + functions: { + execute: cy.stub().resolves({ data: { email: 'test@test.com' } }), + }, + user: { + id: '1', + custom_data: { + roles: ['subscriber'], + }, + }, + }; + + cy.wrap(getUserAdminData({ userId: '1' })).then((response) => { + expect(global.context.functions.execute).to.have.been.calledOnce; + expect(response.email).to.equal('test@test.com'); + }); + }); + + it('Should return data if user is admin', () => { + global.context = { + // @ts-ignore + functions: { + execute: cy.stub().resolves({ data: { email: 'test@test.com' } }), + }, + user: { + id: '1', + custom_data: { + roles: ['admin'], + }, + }, + }; + + cy.wrap(getUserAdminData({ userId: '3211231' })).then((response) => { + expect(global.context.functions.execute).to.have.been.calledOnce; + expect(response.email).to.equal('test@test.com'); + }); + }); +}); diff --git a/site/gatsby-site/i18n/locales/es/translation.json b/site/gatsby-site/i18n/locales/es/translation.json index 655a8d84f4..33c0486a00 100644 --- a/site/gatsby-site/i18n/locales/es/translation.json +++ b/site/gatsby-site/i18n/locales/es/translation.json @@ -318,5 +318,7 @@ "Name": "Nombre", "Creation Date": "Fecha de Creación", "Last modified": "Última modificación", - "Save": "Guardar" + "Save": "Guardar", + "Language": "Idioma", + "Read the Source": "Leer la Fuente" } diff --git a/site/gatsby-site/i18n/locales/fr/translation.json b/site/gatsby-site/i18n/locales/fr/translation.json index ca50f62bdb..22ecdb2f00 100644 --- a/site/gatsby-site/i18n/locales/fr/translation.json +++ b/site/gatsby-site/i18n/locales/fr/translation.json @@ -306,5 +306,6 @@ "Name": "Nom", "Creation Date": "Date de création", "Last modified": "Dernière modification", - "Save": "Enregistrer" + "Save": "Enregistrer", + "Read the Source": "Lire la source" } diff --git a/site/gatsby-site/i18n/locales/ja/translation.json b/site/gatsby-site/i18n/locales/ja/translation.json index 1c17f80f7f..64d827a175 100644 --- a/site/gatsby-site/i18n/locales/ja/translation.json +++ b/site/gatsby-site/i18n/locales/ja/translation.json @@ -291,5 +291,11 @@ "Name": "名前", "Creation Date": "作成日", "Last modified": "最終更新", - "Save": "保存" + "Save": "保存", + "results found": "件の結果が見つかりました", + "More filters": "さらにフィルタ", + "Fewer filters": "フィルタを減らす", + "Tags": "タグ", + "Language": "言語", + "Read the Source": "情報源を読む" } diff --git a/site/gatsby-site/migrations/2024.04.16T20.04.34.delete-dummy-translations.js b/site/gatsby-site/migrations/2024.04.16T20.04.34.delete-dummy-translations.js new file mode 100644 index 0000000000..3c31d0ba36 --- /dev/null +++ b/site/gatsby-site/migrations/2024.04.16T20.04.34.delete-dummy-translations.js @@ -0,0 +1,24 @@ +const languages = require('../i18n/config.json'); + +/** + * + * @param {{context: {client: import('mongodb').MongoClient}}} context + */ + +exports.up = async ({ context: { client } }) => { + const db = client.db('translations'); + + for (const language of languages) { + const name = `reports_${language.code}`; + + console.log(`Deleting dummy translations from ${name}`); + + const translations = db.collection(name); + + const result = await translations.deleteMany({ + text: /^translated-/, + }); + + console.log(`Deleted ${result.deletedCount} dummy translations for ${language.code}`); + } +}; diff --git a/site/gatsby-site/src/components/discover/Actions.js b/site/gatsby-site/src/components/discover/Actions.js index 93d64aeee6..b0f503ab7c 100644 --- a/site/gatsby-site/src/components/discover/Actions.js +++ b/site/gatsby-site/src/components/discover/Actions.js @@ -110,7 +110,7 @@ export default function Actions({ item, toggleFilterByIncidentId = null }) { titleId="report-source" icon={faNewspaper} className="fa-newspaper" - title="Read the Source" + title={t('Read the Source')} /> @@ -203,7 +203,7 @@ export default function Actions({ item, toggleFilterByIncidentId = null }) { titleId="report-hashtag" icon={faHashtag} className="fa-hashtag" - title="Incident ID" + title={t('Incident ID')} /> {item.incident_id} diff --git a/site/gatsby-site/src/components/discover/filterTypes/Classifications.js b/site/gatsby-site/src/components/discover/filterTypes/Classifications.js index 81540aa853..2877da6f3c 100644 --- a/site/gatsby-site/src/components/discover/filterTypes/Classifications.js +++ b/site/gatsby-site/src/components/discover/filterTypes/Classifications.js @@ -29,7 +29,8 @@ function Attribute({ name, refinement, searchResults }) { tabIndex={0} onClick={() => setCollapsed((c) => !c)} > - {name} ({refinement.items.length}){' '} + {name} (~ + {refinement.items.reduce((a, c) => Math.max(a, c.count), refinement.items[0].count)}){' '}