Skip to content

Commit

Permalink
Add tests and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Brandon32 committed Sep 18, 2024
1 parent e5e433a commit 7783e1b
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 31 deletions.
43 changes: 43 additions & 0 deletions blocks/url-decode/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# URL Decode Validator

This block validates that a CaaS indexed URL is able to be decoded.

## Use Case

Select a locale to fetch the query index from.
The block will then attempt to decode the URL and return the result in a table.

## Process

1. **Fetch Query Index**: The block fetches the index (`query-index.json`) based on the selected locale.
2. **Decode URLs**: The URLs from the fetched JSON are decoded using the `validateDecodedUrls` function.
3. **Validation**: Each decoded URL is validated to check if it can be successfully decoded and accessed.
4. **Generate Report**: The results are compiled into a table

## Generate Report

The URL Decode block will create a table with the following columns:

- `path`: The page where the CaaS links are indexed from.
- `validation`: Status of the URL decode.
- `count`: Number of URLs decoded.

## Validation Process

Each entry in the query index processed by parsing the caas-url column and looping through each URL. Each URL is decoded using the `parseEncodedConfig` or `decodeCompressedString` function. If any of the URLs are not able to be decoded, the validation will fail.

## Decode URLs output

The output of the URL decoding process will be an array containing the decoded configurations or `null` if the URL was not able to be decoded.

Example output:

```json
[
{
"path": "/resources/2021-state-work",
"validation": "Valid",
"count": 1
}
]
```
52 changes: 32 additions & 20 deletions blocks/url-decode/url-decode.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ export const createTable = (data) => {
data.forEach((row) => {
const bodyRow = createTag('tr');
headers.forEach((header) => {
bodyRow.append(createTag('td', null, row[header]));
if (row[header] instanceof HTMLElement) {
bodyRow.append(createTag('td', null, row[header]));
} else {
bodyRow.append(createTag('td', null, String(row[header])));
}
});
tbody.append(bodyRow);
});
Expand All @@ -61,7 +65,6 @@ export const createTable = (data) => {
};

export async function decodeUrl(url) {
if (!url) return null;
const encodedConfig = url.split('#')[1];
if (!encodedConfig) return null;

Expand All @@ -78,38 +81,47 @@ async function decodeUrls(data) {
let encodedLinks = [];

try {
encodedLinks = JSON.parse(data);

if (!Array.isArray(encodedLinks)) {
encodedLinks = [encodedLinks];
const parsed = JSON.parse(data);
if (parsed) {
encodedLinks = Array.isArray(parsed) ? parsed : [encodedLinks];
}
} catch (e) {
encodedLinks = [data];
if (data) encodedLinks = [data];
}

const decodedLinks = await Promise.all(encodedLinks.map(decodeUrl));
const decodedLinks = [];
for (const link of encodedLinks) {
try {
const decodedLink = await decodeUrl(link);
decodedLinks.push(decodedLink);
} catch (e) {
decodedLinks.push(null);
}
}

return decodedLinks;
}

async function validateDecodedUrls(data, configColumn) {
return Promise.all(data.map(async (page) => {
const path = createTag('a', { href: page.path }, page.path);
try {
const configs = await decodeUrls(page[configColumn]);
const pageUrl = new URL(page.path, window.location.origin);
if (!window.location.pathname.includes('.html')) {
pageUrl.pathname = pageUrl.pathname.replace('.html', '');
}

for (const config of configs) {
if (!config) return { path, validLink: 'No Config Found' };
const path = createTag('a', { href: pageUrl.href, target: '_blank' }, pageUrl.pathname);
const configs = await decodeUrls(page[configColumn]);
const count = configs.length;

const validLink = Object.keys(config).length > 0;
if (configs.length === 0) return { path, validation: 'No Config Found', count };

if (!validLink) return { path, validLink: 'Empty Config' };
}
for (const [i, config] of configs.entries()) {
if (!config) return { path, validation: `Could not decode link ${i + 1}`, count };

return { path, validLink: 'Yes' };
} catch (e) {
return { path, validLink: 'Could not decode link' };
if (Object.keys(config).length === 0) return { path, validation: 'Empty Config', count };
}

return { path, validation: 'Valid', count };
}));
}

Expand All @@ -120,7 +132,7 @@ async function generateReport(el, configColumn) {

const indexLink = createTag('p', null, [
'Fetching data from ',
createTag('a', { href: queryIndex.href }, queryIndex.href),
createTag('a', { href: queryIndex.href, target: '_blank' }, queryIndex.href),
]);
report.replaceChildren(indexLink);

Expand Down
15 changes: 13 additions & 2 deletions test/blocks/url-decode/mocks/query-index.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"total":4,
"total":6,
"offset":0,
"limit":4,
"limit":6,
"data":[{
"path": "/resources/2021-state-work",
"title": "The 2021 State of Work: How COVID-19 Changed Digital Work",
Expand All @@ -25,6 +25,17 @@
"title": "Learning Manager: Personalized, data-driven learning tech.",
"date": "45470",
"caas-url": "0"
},
{
"path": "/au/customer-success-stories/charles-schwab-case-study.html",
"lastModified": "1726608639",
"robots": "noodp",
"caas-url": "[]"
},
{
"path": "/customer-success-stories",
"title": "Customer Success Stories",
"lastModified": "1726608639"
}],
":type":"sheet"
}
31 changes: 22 additions & 9 deletions test/blocks/url-decode/url-decode.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import waitForElement from '../../helpers/waitForElement.js';
import { LIBS } from '../../../scripts/scripts.js';

const { utf8ToB64 } = await import(`${LIBS}/utils/utils.js`);
const queryIndex = await readFile({ path: './mocks/query-index.json' });

window.lana = { log: () => { } };
const queryIndex = await readFile({ path: './mocks/query-index.json' });

const QUERY_INEDX_LENGTH = 6;

describe('URL Decode', () => {
before(() => {
Expand Down Expand Up @@ -52,7 +54,7 @@ describe('URL Decode', () => {
it('fetches query index', async () => {
const data = await loadQueryIndex('https://business.adobe.com/query-index.json');

expect(data.length).to.equal(4);
expect(data.length).to.equal(QUERY_INEDX_LENGTH);
});

it('creates a table', async () => {
Expand Down Expand Up @@ -81,6 +83,8 @@ describe('URL Decode', () => {
});

it('shows report data', async () => {
const getCell = (row, index) => row.querySelector(`td:nth-child(${index})`).textContent;

const el = document.querySelector('.url-decode');
const submit = el.querySelectorAll('button[type="submit"]');

Expand All @@ -92,13 +96,22 @@ describe('URL Decode', () => {

const rows = table.querySelectorAll('tr');

expect(rows).to.have.length(5);
expect(rows).to.have.length(QUERY_INEDX_LENGTH + 1);
expect(rows[0].querySelector('th').textContent).to.equal('path');
expect(rows[0].querySelector('th:nth-child(2)').textContent).to.equal('validLink');

expect(rows[1].querySelector('td:nth-child(2)').textContent).to.equal('Yes');
expect(rows[2].querySelector('td:nth-child(2)').textContent).to.equal('Could not decode link');
expect(rows[3].querySelector('td:nth-child(2)').textContent).to.equal('Yes');
expect(rows[4].querySelector('td:nth-child(2)').textContent).to.equal('No Config Found');
expect(rows[0].querySelector('th:nth-child(2)').textContent).to.equal('validation');
expect(rows[0].querySelector('th:nth-child(3)').textContent).to.equal('count');

expect(getCell(rows[1], 2)).to.equal('Valid');
expect(getCell(rows[1], 3)).to.equal('1');
expect(getCell(rows[2], 2)).to.equal('Could not decode link 1');
expect(getCell(rows[2], 3)).to.equal('1');
expect(getCell(rows[3], 2)).to.equal('Valid');
expect(getCell(rows[3], 3)).to.equal('1');
expect(getCell(rows[4], 2)).to.equal('No Config Found');
expect(getCell(rows[4], 3)).to.equal('0');
expect(getCell(rows[5], 2)).to.equal('No Config Found');
expect(getCell(rows[5], 3)).to.equal('0');
expect(getCell(rows[6], 2)).to.equal('No Config Found');
expect(getCell(rows[6], 3)).to.equal('0');
});
});

0 comments on commit 7783e1b

Please sign in to comment.