Skip to content

Commit

Permalink
Merge pull request #159 from LambdaTest/dev
Browse files Browse the repository at this point in the history
Dev. -->> Stage
  • Loading branch information
sushobhit-lt authored Nov 5, 2024
2 parents 64e8e5a + 9d3fae5 commit f93e8d7
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/commander/capture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ command
.description('Capture screenshots of static sites')
.argument('<file>', 'Web static config file')
.option('--parallel', 'Capture parallely on all browsers')
.option('--fetch-results [filename]', 'Fetch results and optionally specify an output file, e.g., <filename>.json')
.action(async function(file, _, command) {
let ctx: Context = ctxInit(command.optsWithGlobals());

Expand Down
1 change: 1 addition & 0 deletions src/commander/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ command
.description('Run test commands around SmartUI')
.argument('<command...>', 'Command supplied for running tests')
.option('-P, --port <number>', 'Port number for the server')
.option('--fetch-results [filename]', 'Fetch results and optionally specify an output file, e.g., <filename>.json')
.action(async function(execCommand, _, command) {
let ctx: Context = ctxInit(command.optsWithGlobals());

Expand Down
1 change: 1 addition & 0 deletions src/commander/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ command
.option('-i, --ignoreDir <patterns>', 'Comma-separated list of directories to ignore', val => {
return val.split(',').map(pattern => pattern.trim());
})
.option('--fetch-results [filename]', 'Fetch results and optionally specify an output file, e.g., <filename>.json')
.action(async function(directory, _, command) {
let ctx: Context = ctxInit(command.optsWithGlobals());

Expand Down
4 changes: 4 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ export default {
MOBILE_ORIENTATION_PORTRAIT: 'portrait',
MOBILE_ORIENTATION_LANDSCAPE: 'landscape',

// build status
BUILD_COMPLETE: 'completed',
BUILD_ERROR: 'error',

// CI
GITHUB_API_HOST: 'https://api.github.com',

Expand Down
16 changes: 16 additions & 0 deletions src/lib/ctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export default (options: Record<string, string>): Context => {
let extensionFiles: string;
let ignoreStripExtension: Array<string>;
let ignoreFilePattern: Array<string>;
let fetchResultObj: boolean;
let fetchResultsFileObj: string;
try {
if (options.config) {
config = JSON.parse(fs.readFileSync(options.config, 'utf-8'));
Expand All @@ -41,6 +43,18 @@ export default (options: Record<string, string>): Context => {
extensionFiles = options.files || ['png', 'jpeg', 'jpg'];
ignoreStripExtension = options.removeExtensions || false
ignoreFilePattern = options.ignoreDir || []

if (options.fetchResults) {
if (options.fetchResults !== true && !options.fetchResults.endsWith('.json')) {
console.error("Error: The file extension for --fetch-results must be .json");
process.exit(1);
}
fetchResultObj = true
fetchResultsFileObj = options.fetchResults === true ? 'results.json' : options.fetchResults;
} else {
fetchResultObj = false
fetchResultsFileObj = ''
}
} catch (error: any) {
console.log(`[smartui] Error: ${error.message}`);
process.exit();
Expand Down Expand Up @@ -103,6 +117,8 @@ export default (options: Record<string, string>): Context => {
fileExtension: extensionFiles,
stripExtension: ignoreStripExtension,
ignorePattern: ignoreFilePattern,
fetchResults: fetchResultObj,
fetchResultsFileName: fetchResultsFileObj,
},
cliVersion: version,
totalSnapshots: -1
Expand Down
8 changes: 8 additions & 0 deletions src/lib/httpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ export default class httpClient {
}, log)
}

getScreenshotData(buildId: string, baseline: boolean, log: Logger) {
return this.request({
url: '/screenshot',
method: 'GET',
params: { buildId, baseline }
}, log);
}

finalizeBuild(buildId: string, totalSnapshots: number, log: Logger) {
let params: Record<string, string | number> = {buildId};
if (totalSnapshots > -1) params.totalSnapshots = totalSnapshots;
Expand Down
93 changes: 93 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import fs from 'fs'
import { Context } from '../types.js'
import { chromium, firefox, webkit, Browser } from '@playwright/test'
import constants from './constants.js';
import chalk from 'chalk';

let isPollingActive = false;

export function delDir(dir: string): void {
if (fs.existsSync(dir)) {
Expand Down Expand Up @@ -199,4 +202,94 @@ export function getRenderViewportsForOptions(options: any): Array<Record<string,
...mobileRenderViewports[constants.MOBILE_OS_IOS],
...mobileRenderViewports[constants.MOBILE_OS_ANDROID]
];
}

// Global SIGINT handler
process.on('SIGINT', () => {
if (isPollingActive) {
console.log('Fetching results interrupted. Exiting...');
isPollingActive = false;
} else {
console.log('\nExiting gracefully...');
}
process.exit(0);
});

// Background polling function
export async function startPolling(ctx: Context, task: any): Promise<void> {
ctx.log.info('Fetching results in progress....');
isPollingActive = true;

const intervalId = setInterval(async () => {
if (!isPollingActive) {
clearInterval(intervalId);
return;
}

try {
const resp = await ctx.client.getScreenshotData(ctx.build.id, ctx.build.baseline, ctx.log);

if (!resp.build) {
ctx.log.info("Error: Build data is null.");
clearInterval(intervalId);
isPollingActive = false;
}

fs.writeFileSync(ctx.options.fetchResultsFileName, JSON.stringify(resp, null, 2));
ctx.log.debug(`Updated results in ${ctx.options.fetchResultsFileName}`);

if (resp.build.build_status_ind === constants.BUILD_COMPLETE || resp.build.build_status_ind === constants.BUILD_ERROR) {
clearInterval(intervalId);
ctx.log.info(`Fetching results completed. Final results written to ${ctx.options.fetchResultsFileName}`);
isPollingActive = false;


// Evaluating Summary
let totalScreenshotsWithMismatches = 0;
let totalVariantsWithMismatches = 0;
const totalScreenshots = Object.keys(resp.screenshots || {}).length;
let totalVariants = 0;

for (const [screenshot, variants] of Object.entries(resp.screenshots || {})) {
let screenshotHasMismatch = false;
let variantMismatchCount = 0;

totalVariants += variants.length; // Add to total variants count

for (const variant of variants) {
if (variant.mismatch_percentage > 0) {
screenshotHasMismatch = true;
variantMismatchCount++;
}
}

if (screenshotHasMismatch) {
totalScreenshotsWithMismatches++;
totalVariantsWithMismatches += variantMismatchCount;
}
}

// Display summary
ctx.log.info(
chalk.green.bold(
`\nSummary of Mismatches:\n` +
`${chalk.yellow('Total Variants with Mismatches:')} ${chalk.white(totalVariantsWithMismatches)} out of ${chalk.white(totalVariants)}\n` +
`${chalk.yellow('Total Screenshots with Mismatches:')} ${chalk.white(totalScreenshotsWithMismatches)} out of ${chalk.white(totalScreenshots)}\n` +
`${chalk.yellow('Branch Name:')} ${chalk.white(resp.build.branch)}\n` +
`${chalk.yellow('Project Name:')} ${chalk.white(resp.project.name)}\n` +
`${chalk.yellow('Build ID:')} ${chalk.white(resp.build.build_id)}\n`
)
);
}
} catch (error: any) {
if (error.message.includes('ENOTFOUND')) {
ctx.log.error('Error: Network error occurred while fetching build results. Please check your connection and try again.');
clearInterval(intervalId);
} else {
ctx.log.error(`Error fetching screenshot data: ${error.message}`);
}
clearInterval(intervalId);
isPollingActive = false;
}
}, 5000);
}
4 changes: 4 additions & 0 deletions src/tasks/captureScreenshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import { Context } from '../types.js'
import { captureScreenshots } from '../lib/screenshot.js'
import chalk from 'chalk';
import { updateLogContext } from '../lib/logger.js'
import { startPolling } from '../lib/utils.js';

export default (ctx: Context): ListrTask<Context, ListrRendererFactory, ListrRendererFactory> => {
return {
title: 'Capturing screenshots',
task: async (ctx, task): Promise<void> => {
try {
ctx.task = task;
if (ctx.options.fetchResults) {
startPolling(ctx, task);
}
updateLogContext({task: 'capture'});

let { capturedScreenshots, output } = await captureScreenshots(ctx);
Expand Down
6 changes: 6 additions & 0 deletions src/tasks/exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ import { Context } from '../types.js'
import chalk from 'chalk'
import spawn from 'cross-spawn'
import { updateLogContext } from '../lib/logger.js'
import { startPolling } from '../lib/utils.js'

export default (ctx: Context): ListrTask<Context, ListrRendererFactory, ListrRendererFactory> => {
return {
title: `Executing '${ctx.args.execCommand?.join(' ')}'`,
task: async (ctx, task): Promise<void> => {

if (ctx.options.fetchResults) {
startPolling(ctx, task);
}

updateLogContext({task: 'exec'});

return new Promise((resolve, reject) => {
Expand Down
4 changes: 4 additions & 0 deletions src/tasks/uploadScreenshots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import { Context } from '../types.js';
import { uploadScreenshots } from '../lib/screenshot.js';
import chalk from 'chalk';
import { updateLogContext } from '../lib/logger.js';
import { startPolling } from '../lib/utils.js';

export default (ctx: Context): ListrTask<Context, ListrRendererFactory, ListrRendererFactory> => {
return {
title: 'Uploading screenshots',
task: async (ctx, task): Promise<void> => {
try {
ctx.task = task;
if (ctx.options.fetchResults) {
startPolling(ctx, task);
}
updateLogContext({ task: 'upload' });

await uploadScreenshots(ctx);
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export interface Context {
fileExtension?: Array<string>,
stripExtension?: boolean,
ignorePattern?: Array<string>,
fetchResults?: boolean,
fetchResultsFileName?: string
}
cliVersion: string;
totalSnapshots: number;
Expand Down

0 comments on commit f93e8d7

Please sign in to comment.