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

Add single merged JSON output option #82

Merged
merged 1 commit into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 65 additions & 25 deletions src/automation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface AutomationProps {
serverPort: number;
url: string;
headless: boolean;
output: OutputType;
}

interface SimpleConfig {
Expand All @@ -39,6 +40,11 @@ interface AdvancedConfig {

type Config = AdvancedConfig | SimpleConfig;

export enum OutputType {
CHART = 'chart',
JSON = 'json',
}

type Flows = {
[key: string]: string[];
};
Expand All @@ -63,6 +69,7 @@ export default async function automate({
serverPort,
url,
headless,
output,
}: AutomationProps) {
const MOUNT = 'Mount';

Expand All @@ -71,35 +78,48 @@ export default async function automate({

let errorMessage: string = '';

async function exportResults() {
switch (output) {
case OutputType.CHART: {
await appendJsonToHTML();
}
case OutputType.JSON: {
await exportJsonBundle();
}
}
}

async function getRawResults() {
let files = await fs.readdir(packagePath);
files = files.filter((file) => file.includes('.json'));
let results: { [_: string]: string } = {};
for (const file of files) {
const jsonContents = await fs.readFile(`${packagePath}/${file}`, 'utf8');
results[file] = jsonContents;
}
return results;
}

async function appendJsonToHTML() {
const { JSDOM } = jsdom;
try {
const contents = await fs.readFile(`${packagePath}/index.html`, 'utf8');
const { document } = new JSDOM(`${contents}`).window;
document.querySelectorAll('.json')?.forEach((item) => item.remove());

const files = await fs.readdir(packagePath);
for (const file of files) {
if (file.includes('.json')) {
const jsonContents = await fs.readFile(
`${packagePath}/${file}`,
'utf8'
);
const jsonScript = document.createElement('script');

const idArr = file.split('-');
jsonScript.id =
averageOf > 1
? `${idArr[1]}-${idArr[2]}`
: `${idArr[0]}-${idArr[1]}`;
jsonScript.classList.add('json');
jsonScript.type = 'application/json';

jsonScript.innerHTML = jsonContents;
document.body.appendChild(jsonScript);
}
}
const rawResults = await getRawResults();
Object.keys(rawResults).forEach((fileName) => {
const jsonScript = document.createElement('script');

const idArr = fileName.split('-');
jsonScript.id =
averageOf > 1 ? `${idArr[1]}-${idArr[2]}` : `${idArr[0]}-${idArr[1]}`;
jsonScript.classList.add('json');
jsonScript.type = 'application/json';

jsonScript.innerHTML = rawResults[fileName];
document.body.appendChild(jsonScript);
});
await fs.writeFile(
`${packagePath}/index.html`,
document.documentElement.outerHTML
Expand All @@ -111,6 +131,21 @@ export default async function automate({
}
}

async function exportJsonBundle() {
const rawResults = await getRawResults();

const result: { [_: string]: {} } = {};

Object.keys(rawResults).forEach((fileName) => {
const parsedResult = JSON.parse(rawResults[fileName]);
result[fileName] = parsedResult;
});

const pathName = `${process.cwd()}/${getFileName('react_profile')}`;
await fs.writeFile(pathName, JSON.stringify(result));
printMessage(NOTICE, { log: `Results saved to ${pathName}` });
}

async function calculateAverage() {
try {
const files = await fs.readdir(packagePath);
Expand Down Expand Up @@ -188,7 +223,7 @@ export default async function automate({
);

if (averageOf === automationCount && i === flows.size - 1)
await appendJsonToHTML();
await exportResults();
}
} catch (e) {
errorMessage = 'An error occurred while calculating averages.';
Expand Down Expand Up @@ -356,9 +391,14 @@ export default async function automate({
await browser.close();

if (averageOf > 1 && automationCount === averageOf) await calculateAverage();
else if (averageOf === 1) await appendJsonToHTML();

if (!isServerReady && automationCount === averageOf) await startServer();
else if (averageOf === 1) await exportResults();

if (
!isServerReady &&
automationCount === averageOf &&
output === OutputType.CHART
)
await startServer();

if (errorMessage)
throw printMessage(ERROR, {
Expand Down
49 changes: 30 additions & 19 deletions src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import yargs from 'yargs';
import automate from './automation.js';
import automate, { OutputType } from './automation.js';
import { MessageTypes, printMessage } from './util.js';
import { hideBin } from 'yargs/helpers';
import { deleteJsonFiles } from './file.util.js';
Expand All @@ -16,6 +16,7 @@ interface Options {
port: number;
watch: boolean | string;
headless: boolean;
output: OutputType;
}

const { AUTOMATION_START, AUTOMATION_STOP, ERROR } = MessageTypes;
Expand Down Expand Up @@ -55,6 +56,10 @@ const options = yargs(hideBin(process.argv))
describe: 'generate charts on every new build',
type: 'boolean' || 'string',
})
.option('output', {
describe: 'choose the desired output. Acceptable values: [chart, json]',
type: 'string',
})
.option('headless', {
describe: 'determines if chromium browser window should be visible',
type: 'boolean',
Expand All @@ -68,6 +73,7 @@ const {
port = 1235,
watch = false,
headless = true,
output = OutputType.CHART,
} = <Options>options;

const cwd = path.resolve();
Expand Down Expand Up @@ -95,10 +101,12 @@ function checkShouldAutomate() {
}, 10000);
}

function getStopMessage() {
return `Displaying ${
versionCount++ ? `${versionCount} versions of ` : ''
}charts at: \x1b[1;32mhttp://localhost:${port}\x1b[37m`;
function getStopMessage(output: OutputType) {
return output === OutputType.CHART
? `Displaying ${
versionCount++ ? `${versionCount} versions of ` : ''
}charts at: \x1b[1;32mhttp://localhost:${port}\x1b[37m`
: 'Automation finished';
}

async function handleAutomation() {
Expand All @@ -119,9 +127,10 @@ async function handleAutomation() {
serverPort: port + 1,
url: page,
headless,
output,
});

printMessage(AUTOMATION_STOP, { log: getStopMessage() });
printMessage(AUTOMATION_STOP, { log: getStopMessage(output) });

if (!isServerReady && automationCount === averageOf) isServerReady = true;
}
Expand Down Expand Up @@ -151,22 +160,24 @@ try {
process.exit();
}

setupProxy();
if (output === OutputType.CHART) {
setupProxy();

if (watch) {
const watchDir = typeof watch === 'string' ? `${cwd}/${watch}` : cwd;
if (watch) {
const watchDir = typeof watch === 'string' ? `${cwd}/${watch}` : cwd;

const events = fs.watch(
watchDir,
{ recursive: true }
// node types are saying that fs.watch returns AsyncIterable<string>, but
// it's actually AsyncIterable<{ eventType: string; filename: string }>.
// Have to cast as unknown first to get around this.
) as unknown as AsyncIterable<{ eventType: string; filename: string }>;
const events = fs.watch(
watchDir,
{ recursive: true }
// node types are saying that fs.watch returns AsyncIterable<string>, but
// it's actually AsyncIterable<{ eventType: string; filename: string }>.
// Have to cast as unknown first to get around this.
) as unknown as AsyncIterable<{ eventType: string; filename: string }>;

for await (const { eventType, filename } of events) {
if (eventType === 'change' && !filename.startsWith('node_modules')) {
checkShouldAutomate();
for await (const { eventType, filename } of events) {
if (eventType === 'change' && !filename.startsWith('node_modules')) {
checkShouldAutomate();
}
}
}
}