-
Notifications
You must be signed in to change notification settings - Fork 184
Fix missing Publisher/Admin favicon and Admin copyright year (#4972) #1332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| /* | ||
| * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. | ||
| * | ||
| * WSO2 LLC. licenses this file to you under the Apache License, | ||
| * Version 2.0 (the "License"); you may not use this file except | ||
| * in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
|
|
||
| /** | ||
| * Regression test for issue #4972 bug 2. | ||
| * | ||
| * The Admin portal's Copyright component was not rolled forward to 2026 when | ||
| * Publisher and DevPortal were updated, so the rendered footer showed | ||
| * "© 2025 WSO2 LLC". This test scans the defaultMessage string for the | ||
| * Base.Footer.Footer.product_details intl key in each portal's source and | ||
| * asserts it carries the current (expected) year. The expected year is | ||
| * derived from the Publisher's Footer.jsx — the portal that was correct | ||
| * at the time the bug was reported — so the test auto-updates when the | ||
| * year is rolled forward next time, as long as Publisher is rolled first. | ||
| */ | ||
| const fs = require('fs'); | ||
|
Check warning on line 31 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/copyrightYear.test.js
|
||
| const path = require('path'); | ||
|
Check warning on line 32 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/copyrightYear.test.js
|
||
|
|
||
| const portalsRoot = path.resolve(__dirname, '../../../../../../../..'); | ||
|
|
||
| const PRODUCT_DETAILS_LINE = /defaultMessage=['"]([^'"]*WSO2 API-M[^'"]*WSO2 LLC)['"]/; | ||
|
|
||
| function readProductDetailsDefault(file) { | ||
| const src = fs.readFileSync(file, 'utf8'); | ||
| const m = src.match(PRODUCT_DETAILS_LINE); | ||
| if (!m) { | ||
| throw new Error(`Could not find product_details defaultMessage in ${file}`); | ||
| } | ||
| return m[1]; | ||
| } | ||
|
|
||
| const sources = { | ||
| publisher: path.join( | ||
| portalsRoot, | ||
| 'publisher/src/main/webapp/source/src/app/components/Base/Footer/Footer.jsx', | ||
| ), | ||
| admin: path.join( | ||
| portalsRoot, | ||
| 'admin/src/main/webapp/source/src/app/components/Base/index.jsx', | ||
| ), | ||
| devportal: path.join( | ||
| portalsRoot, | ||
| 'devportal/src/main/webapp/source/src/app/components/Base/index.jsx', | ||
| ), | ||
| }; | ||
|
|
||
| describe('Issue #4972 — portal Copyright defaultMessage carries the expected year', () => { | ||
| const publisherMessage = readProductDetailsDefault(sources.publisher); | ||
| const yearMatch = publisherMessage.match(/©\s*(\d{4})\s*WSO2 LLC/); | ||
| const expectedYear = yearMatch && yearMatch[1]; | ||
|
Check warning on line 65 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/copyrightYear.test.js
|
||
|
|
||
| it('publisher Footer has a parseable © <year> WSO2 LLC marker', () => { | ||
| expect(expectedYear).toMatch(/^\d{4}$/); | ||
| }); | ||
|
|
||
| it('expected year is 2026 or later (bug was: stale 2025)', () => { | ||
| expect(Number(expectedYear)).toBeGreaterThanOrEqual(2026); | ||
| }); | ||
|
|
||
| it.each([ | ||
| ['admin', sources.admin], | ||
| ['devportal', sources.devportal], | ||
| ])('%s Copyright defaultMessage carries the same year as publisher', (portal, file) => { | ||
| const msg = readProductDetailsDefault(file); | ||
| expect(msg).toContain(`© ${expectedYear} WSO2 LLC`); | ||
| expect(msg).not.toMatch(/©\s*2025\s*WSO2 LLC/); | ||
| }); | ||
|
|
||
| it('admin Copyright defaultMessage matches the expected product detail line exactly', () => { | ||
| // Catches e.g. a typo in the product name or a reverted version bump. | ||
| const msg = readProductDetailsDefault(sources.admin); | ||
| expect(msg).toBe(`WSO2 API-M v4.7.0 | © ${expectedYear} WSO2 LLC`); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| /* | ||
| * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. | ||
| * | ||
| * WSO2 LLC. licenses this file to you under the Apache License, | ||
| * Version 2.0 (the "License"); you may not use this file except | ||
| * in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
|
|
||
| /** | ||
| * Regression test for issue #4972 bug 1. | ||
| * | ||
| * The Publisher and Admin portal HTML entry points were missing the modern | ||
| * <link rel="icon" type="image/png"> tag that the DevPortal already emitted. | ||
| * Chromium therefore never requested a favicon for Publisher/Admin and the | ||
| * tab icon was blank. The fix adds the tag alongside the legacy | ||
| * <link rel="shortcut icon"> in every portal entry template. | ||
| * | ||
| * This test reads the four Publisher + Admin templates straight from source | ||
| * and asserts that both link tags are present. The DevPortal baselines are | ||
| * asserted too so the test also catches any regression there. | ||
| */ | ||
| const fs = require('fs'); | ||
|
Check warning on line 32 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/favicon.test.js
|
||
| const path = require('path'); | ||
|
Check warning on line 33 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/favicon.test.js
|
||
|
|
||
| const portalsRoot = path.resolve(__dirname, '../../../../../../../..'); | ||
|
|
||
| const templates = [ | ||
| { | ||
| portal: 'publisher', | ||
| file: path.join(portalsRoot, 'publisher/src/main/webapp/site/public/pages/index.jsp.hbs'), | ||
| expectedHref: '/site/public/images/_favicon.png', | ||
| }, | ||
| { | ||
| portal: 'publisher', | ||
| file: path.join(portalsRoot, 'publisher/src/main/webapp/publisher/index.html'), | ||
| expectedHref: '/site/public/images/_favicon.png', | ||
| }, | ||
| { | ||
| portal: 'admin', | ||
| file: path.join(portalsRoot, 'admin/src/main/webapp/site/public/pages/index.jsp.hbs'), | ||
| expectedHref: '/site/public/images/_favicon.png', | ||
| }, | ||
| { | ||
| portal: 'admin', | ||
| file: path.join(portalsRoot, 'admin/src/main/webapp/admin/index.ejs'), | ||
| expectedHref: '/site/public/images/_favicon.png', | ||
| }, | ||
| { | ||
| portal: 'devportal', | ||
| file: path.join(portalsRoot, 'devportal/src/main/webapp/site/public/pages/index.jsp.hbs'), | ||
| expectedHref: '/site/public/images/_favicon.png', | ||
| }, | ||
| { | ||
| portal: 'devportal', | ||
| file: path.join(portalsRoot, 'devportal/src/main/webapp/devportal/index.ejs'), | ||
| expectedHref: '/site/public/images/_favicon.png', | ||
| }, | ||
| ]; | ||
|
|
||
| describe('Issue #4972 — portal HTML templates emit both favicon link tags', () => { | ||
| templates.forEach(({ portal, file, expectedHref }) => { | ||
| describe(`${portal}: ${path.basename(file)}`, () => { | ||
| let markup; | ||
| beforeAll(() => { | ||
| markup = fs.readFileSync(file, 'utf8'); | ||
| }); | ||
|
|
||
| it('emits the legacy <link rel="shortcut icon"> tag', () => { | ||
| expect(markup).toMatch(/<link[^>]+rel=["']shortcut icon["'][^>]*>/); | ||
| }); | ||
|
|
||
| it('emits the modern <link rel="icon" type="image/png"> tag', () => { | ||
| expect(markup).toMatch(/rel=["']icon["']\s+type=["']image\/png["']/); | ||
| }); | ||
|
|
||
| it('points the modern <link rel="icon"> at _favicon.png', () => { | ||
| // The surrounding <link ...> may embed templating tokens like | ||
| // <%= context%> so we don't try to match the whole tag — we | ||
| // just assert _favicon.png is referenced in the file alongside | ||
| // a rel="icon" tag (asserted separately above). | ||
| expect(markup).toContain(expectedHref); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| it('asserts the _favicon.png asset exists in every portal public/images directory', () => { | ||
| const assetPaths = [ | ||
| path.join(portalsRoot, 'publisher/src/main/webapp/site/public/images/_favicon.png'), | ||
| path.join(portalsRoot, 'admin/src/main/webapp/site/public/images/_favicon.png'), | ||
| path.join(portalsRoot, 'devportal/src/main/webapp/site/public/images/_favicon.png'), | ||
| ]; | ||
| assetPaths.forEach((p) => { | ||
| expect(fs.existsSync(p)).toBe(true); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,104 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * WSO2 LLC. licenses this file to you under the Apache License, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Version 2.0 (the "License"); you may not use this file except | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * in compliance with the License. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * You may obtain a copy of the License at | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Unless required by applicable law or agreed to in writing, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * software distributed under the License is distributed on an | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * KIND, either express or implied. See the License for the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * specific language governing permissions and limitations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * under the License. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Regression test for issue #4972 bug 2 — locale files. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Beyond the JSX defaultMessage, the admin en.json and fr.json carried a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * stale "© 2025 WSO2 LLC" at locales/en.json:409 / fr.json in the shipped | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * pack. That string is what users actually see, so we scan every locale | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * file in every portal's webapp and assert any translation of | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Base.Footer.Footer.product_details either matches the current expected | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * year or is an intentionally-blank fallback (which react-intl falls back | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * to the defaultMessage for). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fs = require('fs'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 30 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/localeYear.test.js
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const path = require('path'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 31 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/localeYear.test.js
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const portalsRoot = path.resolve(__dirname, '../../../../../../../..'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const PRODUCT_DETAILS_KEY = 'Base.Footer.Footer.product_details'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const PRODUCT_DETAILS_LINE = /defaultMessage=['"]([^'"]*WSO2 API-M[^'"]*WSO2 LLC)['"]/; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function readProductDetailsDefault(file) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const src = fs.readFileSync(file, 'utf8'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const m = src.match(PRODUCT_DETAILS_LINE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return m && m[1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 41 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/localeYear.test.js
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function expectedYear() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const publisherMsg = readProductDetailsDefault( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path.join( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| portalsRoot, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'publisher/src/main/webapp/source/src/app/components/Base/Footer/Footer.jsx', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const match = publisherMsg && publisherMsg.match(/©\s*(\d{4})\s*WSO2 LLC/); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 51 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/localeYear.test.js
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return match && match[1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Check warning on line 52 in portals/publisher/src/main/webapp/source/Tests/Unit/Issue4972/localeYear.test.js
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function collectLocaleFiles() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const portals = ['publisher', 'admin', 'devportal']; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const out = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| portals.forEach((portal) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dir = path.join(portalsRoot, `${portal}/src/main/webapp/site/public/locales`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!fs.existsSync(dir)) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fs.readdirSync(dir).forEach((f) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (f.endsWith('.json')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| out.push({ portal, locale: f, file: path.join(dir, f) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return out; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe('Issue #4972 — locale files for Base.Footer.Footer.product_details', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const year = expectedYear(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const localeFiles = collectLocaleFiles(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('the publisher defaultMessage exposes an expected year', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(year).toMatch(/^\d{4}$/); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(Number(year)).toBeGreaterThanOrEqual(2026); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('locale directories were discovered', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(localeFiles.length).toBeGreaterThan(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
+80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fail when an expected portal locale directory is missing. Line 60 silently skips missing portal locale dirs, and Line 80 only proves that at least one locale file exists. That can drop admin/devportal coverage without failing this regression test. Proposed test hardening+const PORTALS = ['publisher', 'admin', 'devportal'];
+
function collectLocaleFiles() {
- const portals = ['publisher', 'admin', 'devportal'];
const out = [];
- portals.forEach((portal) => {
+ PORTALS.forEach((portal) => {
const dir = path.join(portalsRoot, `${portal}/src/main/webapp/site/public/locales`);
- if (!fs.existsSync(dir)) return;
+ if (!fs.existsSync(dir)) {
+ throw new Error(`Missing locale directory for ${portal}: ${dir}`);
+ }
fs.readdirSync(dir).forEach((f) => {
if (f.endsWith('.json')) {
out.push({ portal, locale: f, file: path.join(dir, f) });
}
});
@@
it('locale directories were discovered', () => {
expect(localeFiles.length).toBeGreaterThan(0);
+ expect(new Set(localeFiles.map(({ portal }) => portal))).toEqual(new Set(PORTALS));
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| describe.each(localeFiles)('$portal/locales/$locale', ({ file }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let bundle; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| beforeAll(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bundle = JSON.parse(fs.readFileSync(file, 'utf8')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('if product_details is present, it is non-null and a string', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (PRODUCT_DETAILS_KEY in bundle) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(typeof bundle[PRODUCT_DETAILS_KEY]).toBe('string'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| it('if product_details has a translation, it carries the current year', () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const value = bundle[PRODUCT_DETAILS_KEY]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Empty string is allowed — react-intl falls back to defaultMessage. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (value && value.trim() !== '') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(value).toContain(`© ${year} WSO2 LLC`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expect(value).not.toMatch(/©\s*2025\s*WSO2 LLC/); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tie
_favicon.pngto the modern favicon link.The current assertions can pass if
_favicon.pngappears elsewhere while the modern<link rel="icon">points to a different asset. Match a single link tag containingrel,type, and the expectedhref.Proposed test tightening
🤖 Prompt for AI Agents