Skip to content
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

refactor(docs): remove numberOfPages from broken link test #2945

Merged
merged 9 commits into from
Nov 18, 2022
18 changes: 1 addition & 17 deletions docs/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,6 @@ export default defineConfig({
setupNodeEvents(on, config) {
// implement node event listeners here
on('task', {
readSitemapLinks: async () => {
if (fs.existsSync('./public/sitemap.xml')) {
const siteMapContent = fs.readFileSync(
'./public/sitemap.xml',
'utf8'
);
const sitemapLinks: string[] = await sitemapUrls.extractUrls(
siteMapContent
);
return sitemapLinks.map((link) =>
link
.replace(`${BASE_URL}/`, '')
.replace('https://www.dev.ui.docs.amplify.aws/', '')
.replace('https://ui.docs.amplify.aws/', '')
);
}
},
log: (message) => {
console.log(message);
return null;
Expand All @@ -34,5 +17,6 @@ export default defineConfig({
baseUrl: BASE_URL,
numTestsKeptInMemory: 1,
supportFile: false,
video: false,
},
});
170 changes: 108 additions & 62 deletions docs/cypress/e2e/links.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,98 +10,138 @@ type EvtName =
| 'REQUESTING'
| 'RETURNING'
| 'NO_HREF';
let allLinks: string[] = [];
const numberOfLinks = 116;
const requestedLinks: Set<string> = new Set();

before(() => {
cy.task('readSitemapLinks').then((links: string[]) => {
allLinks = allLinks.concat(links);
});
});
describe(`All pages on Sitemap`, () => {
let allLinks = [];
const requestedLinks: Set<string> = new Set();

describe('Local Sitemap', () => {
it('should have 119 links', () => {
expect(allLinks.length).to.eq(numberOfLinks);
before(() => {
cy.request('sitemap.xml').then((response) => {
allLinks = Cypress.$(response.body)
.find('loc')
.toArray()
.map((el) => el.innerText)
.map((link) =>
link
.replace(`${BASE_URL}/`, '')
.replace('https://www.dev.ui.docs.amplify.aws/', '')
.replace('https://ui.docs.amplify.aws/', '')
);
});
});
});

describe(`All pages on Sitemap`, () => {
it.each(numberOfLinks)('all links on page %k should work', (i: string) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the trade-off. allLinks is empty when it.each sets up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, since it's a giant test, which takes a lot of memory, the Amplify Hosting Build pipeline failed on my personal account. Tried a few times, never succeed, but it works on my local machine.

But I wonder if we can try it in our Dev since its build's been quicker and less issue than my personal account.

const link = allLinks[i];
cy.task('log', `🧪[TESTING...] page ${BASE_URL}/${link}`);
cy.visit(link || '/');
cy.get('a').each((el) => hrefWorks(el, link));
it('should succesfully load each url in the sitemap', () => {
allLinks.forEach((link, idx) => {
cy.task('log', `🧪[TESTING...] page #${idx} ${BASE_URL}/${link}`);
cy.visit({ url: link || '/', qs: { cypress: true } });

/** Check all the internal links */
cy.get(`a[href^='/']`).each((el) => hrefOnSitemap(el, link, allLinks));

/** Check all the external links and internal links with hash */
cy.get(`a:not([href^='/']), a[href*='#']`).each((el) =>
hrefWorks(el, link, requestedLinks)
);
});
});
});

export {};

function hrefWorks(htmlTag: JQuery<HTMLElement>, link: string): void {
function hrefOnSitemap(
htmlTag: JQuery<HTMLElement>,
link: string,
allLinks: string[]
) {
const tagHref: string = htmlTag.prop('href');
const tagText: string = htmlTag.prop('text');
const tagName: string = htmlTag.prop('tagName');
const tagHash: string = htmlTag.prop('hash');
const pureHref = tagHref.replace(tagHash, '');
/**
* The following logic is to make the list cy.request() to save memory when build in Amplify Hosting.
*/
if (
/** pureHref is included in Sitemap, which is already tested by cy.visit(). */
allLinks.includes(`${pureHref.replace(`${BASE_URL}/`, '')}`)
) {
expect(`${pureHref.replace(`${BASE_URL}/`, '')}`).to.oneOf(allLinks);
logMessage({
evtName: 'SKIPPING_SITEMAP',
link,
tagHref: pureHref,
tagName,
tagText,
});
} else {
/**
* pureHref is platform neutral, which would be redirected to react by default and return a 308.
* To prevent the 308, we manually redirect it to react
*/
expect(`${pureHref.replace(`${BASE_URL}/`, 'react/')}`).to.oneOf(allLinks);
logMessage({
evtName: 'SKIPPING_SITEMAP',
link,
tagHref: pureHref,
tagName,
tagText,
});
}
}

function hrefWorks(
htmlTag: JQuery<HTMLElement>,
link: string,
requestedLinks: Set<string>
): void {
const tagHref: string = htmlTag.prop('href');
const tagText: string = htmlTag.prop('text');
const tagName: string = htmlTag.prop('tagName');
let pureHref: string;

if (tagHref) {
pureHref = tagHref.replace(tagHash, ''); // TODO: add test to validate links with a hash tag.
logMessage({ evtName: 'CHECKING', link, pureHref, tagName, tagText });
logMessage({
evtName: 'CHECKING',
link,
tagHref,
tagName,
tagText,
});

if (allLinks.includes(`${pureHref.replace(`${BASE_URL}/`, '')}`)) {
expect(`${pureHref.replace(`${BASE_URL}/`, '')}`).to.oneOf(allLinks);
logMessage({
evtName: 'SKIPPING_SITEMAP',
link,
pureHref,
tagName,
tagText,
});
} else if (
allLinks.includes(`${pureHref.replace(`${BASE_URL}/`, 'react/')}`)
) {
expect(`${pureHref.replace(`${BASE_URL}/`, 'react/')}`).to.oneOf(
allLinks
);
logMessage({
evtName: 'SKIPPING_SITEMAP',
link,
pureHref,
tagName,
tagText,
});
} else if (
VALIDATED_LINKS.includes(pureHref) ||
VALIDATED_LINKS.includes(`${pureHref.replace(BASE_URL, '')}`) ||
requestedLinks.has(pureHref)
if (
/** pureHref is listed in the VALIDATED_LINKS list or already requested in the test. */
VALIDATED_LINKS.includes(tagHref) ||
VALIDATED_LINKS.includes(`${tagHref.replace(BASE_URL, '')}`) ||
requestedLinks.has(tagHref)
) {
logMessage({
evtName: 'SKIPPING_VALIDATED',
link,
pureHref,
tagHref,
tagName,
tagText,
});
} else {
const requestMethod = REQUEST_GET_LINKS.includes(pureHref)
const requestMethod = REQUEST_GET_LINKS.includes(tagHref)
? 'GET'
: 'HEAD';
logMessage({
evtName: 'REQUESTING',
link,
pureHref,
tagHref,
tagName,
tagText,
});
requestedLinks.add(pureHref);
requestedLinks.add(tagHref);
cy.request({
url: pureHref,
url: tagHref,
followRedirect: false,
method: requestMethod,
qs: { cypress: true },
}).then(({ status }) => {
logMessage({
evtName: 'RETURNING',
link,
pureHref,
tagHref,
tagName,
tagText,
status,
Expand All @@ -111,21 +151,27 @@ function hrefWorks(htmlTag: JQuery<HTMLElement>, link: string): void {
});
}
} else if (tagName === 'A') {
logMessage({ evtName: 'NO_HREF', link, pureHref, tagName, tagText });
logMessage({
evtName: 'NO_HREF',
link,
tagHref,
tagName,
tagText,
});
}
}

function logMessage({
evtName,
link,
pureHref,
tagHref,
status,
tagName,
tagText,
}: {
evtName: EvtName;
link: string;
pureHref: string;
tagHref: string;
status?: number;
tagName: string;
tagText: string;
Expand All @@ -134,35 +180,35 @@ function logMessage({
case 'CHECKING':
return cy.task(
'log',
`🔍[CHECKING...] ${pureHref} from ${tagName} tag ${
`🔍[CHECKING...] ${tagHref} from ${tagName} tag ${
tagText ? `"${tagText}"` : ''
} on ${BASE_URL}/${link}`
);
case 'SKIPPING_SITEMAP':
return cy.task(
'log',
`⏭[SKIPPING...] ${pureHref} from ${tagName} tag ${
`⏭[SKIPPING...] ${tagHref} from ${tagName} tag ${
tagText ? `"${tagText}"` : ''
} on ${BASE_URL}/${link} because it's included in Sitemap and already tested.`
);
case 'SKIPPING_VALIDATED':
return cy.task(
'log',
`⏭[SKIPPING...] ${pureHref} from ${tagName} tag ${
`⏭[SKIPPING...] ${tagHref} from ${tagName} tag ${
tagText ? `"${tagText}"` : ''
} on ${BASE_URL}/${link} because it's already validated.`
);
case 'REQUESTING':
return cy.task(
'log',
`📞[REQUESTING...] ${pureHref} from ${tagName} tag ${
`📞[REQUESTING...] ${tagHref} from ${tagName} tag ${
tagText ? `"${tagText}"` : ''
} on ${BASE_URL}/${link}`
);
case 'RETURNING':
return cy.task(
'log',
`↩️ [RETURNING STATUS...] ${status} for ${pureHref}`
`↩️ [RETURNING STATUS...] ${status} for ${tagHref}`
);
case 'NO_HREF':
return cy.task(
Expand Down