diff --git a/concourse/book-scan/script.bash b/concourse/book-scan/script.bash
index 4ebd30710a..3fff4a8f6e 100755
--- a/concourse/book-scan/script.bash
+++ b/concourse/book-scan/script.bash
@@ -6,6 +6,6 @@ cloudfront_environment=$(< cloudfront-environment/version.txt)
cd rex-web
-yarn
+yarn install --network-timeout 60000
node script/entry.js domVisitor errorsExist --rootUrl="https://$cloudfront_environment" --queryString="validateLinks"
diff --git a/concourse/create-test-plan-configs/task.yml b/concourse/create-test-plan-configs/task.yml
index 41e2419d3d..861594db39 100644
--- a/concourse/create-test-plan-configs/task.yml
+++ b/concourse/create-test-plan-configs/task.yml
@@ -30,5 +30,5 @@ run:
- |
DEST=$(pwd)/test-plans
cd rex-web/concourse/create-test-plan-configs
- yarn install
+ yarn install --network-timeout 60000
./script.js $DEST
diff --git a/concourse/lighthouse/script.bash b/concourse/lighthouse/script.bash
new file mode 100755
index 0000000000..bbe032aee1
--- /dev/null
+++ b/concourse/lighthouse/script.bash
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+set -euxo pipefail
+
+base_dir=$(pwd)
+
+cd rex-web
+
+yarn install --network-timeout 60000
+
+node script/entry.js lighthouse --pages="$LIGHTHOUSE_PAGES" \
+ --mostRecentReportDir="$base_dir/$LIGHTHOUSE_MOST_RECENT_REPORT_DIR" \
+ --reportDir="$base_dir/$LIGHTHOUSE_REPORT_DIR"
diff --git a/concourse/lighthouse/task.yml b/concourse/lighthouse/task.yml
new file mode 100644
index 0000000000..bd11e11979
--- /dev/null
+++ b/concourse/lighthouse/task.yml
@@ -0,0 +1,26 @@
+---
+platform: linux
+
+image_resource:
+ type: docker-image
+ source:
+ password: ((dockerhub-password))
+ username: ((dockerhub-username))
+ repository: openstax/rex-web
+ tag: CI-2023-08-10
+
+inputs:
+- name: rex-web
+- name: most-recent-lighthouse-report
+outputs:
+- name: lighthouse-report
+
+params:
+ AWS_ACCESS_KEY_ID: ((prod-aws-access-key-id))
+ AWS_SECRET_ACCESS_KEY: ((prod-aws-secret-access-key))
+ LIGHTHOUSE_PAGES: ((lighthouse-pages))
+ LIGHTHOUSE_MOST_RECENT_REPORT_DIR: most-recent-lighthouse-report
+ LIGHTHOUSE_REPORT_DIR: lighthouse-report
+
+run:
+ path: rex-web/concourse/lighthouse/script.bash
diff --git a/package.json b/package.json
index 853baf33e9..020c3dd526 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,7 @@
"test:prerender:specs": "REACT_APP_ENV=test SERVER_MODE=built jest --testPathPattern=\"(\\.|/)prerenderspec\\.tsx?\" --config jest-puppeteer.config.json",
"test:prerender:browser": "REACT_APP_ENV=test SERVER_MODE=built jest --testPathPattern=\"(\\.|/)browserspec\\.tsx?\" --config jest-puppeteer.config.json -i",
"test:prerender:screenshots": "REACT_APP_ENV=test SERVER_MODE=built jest --testPathPattern=\"(\\.|/)screenshotspec\\.tsx?\" --config jest-puppeteer.config.json",
- "test:lighthouse-manual": "REACT_APP_ENV=test lighthouse --view --config-path=./src/test/audits/index.js",
+ "test:lighthouse:manual": "REACT_APP_ENV=test lighthouse --view --config-path=./src/test/audits/index.js",
"analyze:bundle": "craco build && source-map-explorer 'build/static/js/*.js' -m",
"analyze:dom": "node ./script/entry.js domVisitor",
"heroku-postbuild": "npm run-script build:clean"
diff --git a/playwright/src/fixtures/content.page.ts b/playwright/src/fixtures/content.page.ts
index f47c972a8d..191bfd2dff 100644
--- a/playwright/src/fixtures/content.page.ts
+++ b/playwright/src/fixtures/content.page.ts
@@ -90,6 +90,13 @@ class ContentPage {
.addCookies([{ name: 'nudge_study_guides_date', value: current_date, url: this.page.url() }])
}
+ async canonical() {
+ // Return canonical link of the current page
+ let canonicalPageSelector = await this.page.$('[rel="canonical"]')
+ const canonicalPage = await canonicalPageSelector.evaluate((e) => e.getAttribute('href'))
+ return canonicalPage
+ }
+
async colorLocator(color: string) {
// Return locator of the color
if (color === 'blue') {
diff --git a/playwright/src/fixtures/toc.ts b/playwright/src/fixtures/toc.ts
index 0b5f52c447..06e60a8584 100644
--- a/playwright/src/fixtures/toc.ts
+++ b/playwright/src/fixtures/toc.ts
@@ -89,7 +89,7 @@ class TOC {
if ((await titleAfterClick) != (await titleBeforeClick)) {
return
} else {
- sleep(1)
+ sleep(2)
}
} else {
console.log('The page number specified exceeds the total pages in the book')
diff --git a/playwright/tests/rex-test/rex.behaviorspec.ts b/playwright/tests/rex-test/rex.behaviorspec.ts
index dc254353e1..29b73b1a7b 100644
--- a/playwright/tests/rex-test/rex.behaviorspec.ts
+++ b/playwright/tests/rex-test/rex.behaviorspec.ts
@@ -576,3 +576,101 @@ test('MH page dropdown filters', async ({ page, isMobile }) => {
const highlightcount = await bookPage.highlightCount()
expect(highlightcount).toBe(2)
})
+
+
+test('C543224 canonicals for books with no shared content', async ({ page, isMobile, browserName }) => {
+ test.skip(isMobile as boolean, 'test only desktop resolution')
+ test.skip(browserName == 'webkit', 'test only chrome')
+ test.skip(browserName == 'firefox', 'test only chrome')
+
+ // GIVEN: Open Rex page
+ const bookPage = new ContentPage(page)
+ const path = '/books/chemistry-2e/pages/1-1-chemistry-in-context'
+ await bookPage.open(path)
+
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/chemistry-2e/pages/1-1-chemistry-in-context')
+
+ // WHEN: click EOC page
+ const Toc = new TOC(page)
+ await Toc.pageClick(9)
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/chemistry-2e/pages/1-key-equations')
+
+ // WHEN: Click EOB page
+ await Toc.pageClick(221)
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/chemistry-2e/pages/e-water-properties')
+
+ // WHEN: click nested EOB page
+ await Toc.pageClick(230)
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/chemistry-2e/pages/chapter-1')
+})
+
+
+test('C543225 canonicals for pages derived from another book', async ({ page, isMobile, browserName }) => {
+ test.skip(isMobile as boolean, 'test only desktop resolution')
+ test.skip(browserName == 'webkit', 'test only chrome')
+ test.skip(browserName == 'firefox', 'test only chrome')
+
+ // GIVEN: Open Rex page derived from another book
+ const bookPage = new ContentPage(page)
+ const path = '/books/preparing-for-college-success/pages/2-1-why-college'
+ await bookPage.open(path)
+
+ // THEN: Canonical page points to original content
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/college-success-concise/pages/1-1-why-college')
+
+ // WHEN: click EOC page from a chapter derived from another book
+ const Toc = new TOC(page)
+ await Toc.pageClick(14)
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/preparing-for-college-success/pages/2-summary')
+
+
+ // WHEN: Open page unique to this book
+ await Toc.pageClick(3)
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/preparing-for-college-success/pages/1-2-your-academic-journey-and-personal-story')
+
+ // WHEN: Open EOC page from the chapter unique to this book
+ await Toc.pageClick(6)
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/preparing-for-college-success/pages/1-family-friends-matter')
+
+ // WHEN: Open EOB page from the chapter unique to this book
+ await Toc.pageClick(71)
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/preparing-for-college-success/pages/index')
+})
+
+test('C543225 canonicals for old editions point to the latest edition', async ({ page, isMobile, browserName }) => {
+ test.skip(isMobile as boolean, 'test only desktop resolution')
+ test.skip(browserName == 'webkit', 'test only chrome')
+ test.skip(browserName == 'firefox', 'test only chrome')
+
+ // GIVEN: Open older edition of Rex page derived from another book
+ const bookPage = new ContentPage(page)
+ const path = '/books/principles-macroeconomics-2e/pages/1-introduction'
+ await bookPage.open(path)
+ // THEN: Canonical page points to latest edition of the original content
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/principles-economics-3e/pages/1-introduction')
+
+ // WHEN: Open older edition of EOC page of a book derived from another book
+ const Toc = new TOC(page)
+ await Toc.pageClick(7)
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/principles-macroeconomics-2e/pages/1-key-concepts-and-summary')
+
+ // WHEN: Open older edition of nested EOB page of a book derived from another book
+ await Toc.pageClick(243)
+ // THEN: Canonical page points to itself
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/principles-macroeconomics-2e/pages/chapter-2')
+
+
+ // WHEN: Open older edition of appendix page of a book derived from another book
+ await Toc.pageClick(241)
+ // THEN: Canonical page points to latest edition of the original content
+ expect(await bookPage.canonical()).toBe('https://openstax.org/books/principles-economics-3e/pages/d-the-expenditure-output-model')
+})
diff --git a/public/index.html b/public/index.html
index f544983b2d..6832197527 100644
--- a/public/index.html
+++ b/public/index.html
@@ -10,26 +10,8 @@
-->
-
-
-
+
-