Skip to content

Commit

Permalink
Pac file test (#1829)
Browse files Browse the repository at this point in the history
* PAC file support via PERCY_PAC_FILE_URL env var (#1784)

* PAC file support via PERCY_PAC_FILE_PATH env var

* Added pac-proxy-agent lib as dependency

* typo fixes

* PR comments addressed

* Moved pac-proxy-agent lib dependency to percy client instead of main package

* Resolved conflicts

* Fix import in ternary statement (#1779)

* Fix import in ternary statement

* Added comment

* Typo fix in comment

Co-authored-by: ninadbstack <[email protected]>

* Removed trailing space

---------

Co-authored-by: ninadbstack <[email protected]>

* v1.30.3-alpha.0

* Cli bundling import fix (#1785)

* Fix import in ternary statement

* Added comment

* Typo fix in comment

Co-authored-by: ninadbstack <[email protected]>

* Removed trailing space

* Fix import of PERCY_DOM in line w ESM req

* made a minor comment change

---------

Co-authored-by: ninadbstack <[email protected]>

* v1.30.3-alpha.1

* v1.30.3-alpha.2

* Cli bundling import fix (#1794)

* Fix import in ternary statement

* Added comment

* Typo fix in comment

Co-authored-by: ninadbstack <[email protected]>

* Removed trailing space

* Fix import of PERCY_DOM in line w ESM req

* made a minor comment change

* Added PERCY_FORCE_DIRNAME flag in getPackageJSON util method

* Env var name updated to PERCY_FORCE_EXECUTABLE_DIRNAME

Co-authored-by: ninadbstack <[email protected]>

---------

Co-authored-by: ninadbstack <[email protected]>

* v1.30.3-alpha.3

* Added PERCY_FORCE_PKG_VALUE in place of PERCY_FORCE_DIRNAME (#1799)

* Added PERCY_FORCE_PKG_VALUE in place of PERCY_FORCE_DIRNAME

* Bumped the percy tag to 1.30.3-alpha.4

* Added PERCY_FORCE_PKG_VALUE in env file and using from there (#1800)

* Added PERCY_FORCE_PKG_VALUE in place of PERCY_FORCE_DIRNAME

* Bumped the percy tag to 1.30.3-alpha.4

* Added PERCY_FORCE_PKG_VALUE in env file and using from there

* Bumped the percy tag to 1.30.3-alpha.5

* fixed linting

* client-fix

* upload-cli-fix

* cli-build-fix

* cli-exec-fix

* cli-snapshot-fix

* MERGE_COMMIT

* MERGE_COMMIT

* Proxy.test

* proxy

* core fixed

* client package

* client package

* client package

* client package

* client package

* lock file

* pac-packages

* pac-packages

* pac-packages

* lint pass

* lint pass

* lint pass

* lint pass

* proxy pass

* client pass

* api-test

* api-test

* percy-test-cases

* Removed find revision

* lint-fix

---------

Co-authored-by: Khushhal Maheshwari <[email protected]>
Co-authored-by: ninadbstack <[email protected]>
Co-authored-by: Ninad Sheth <[email protected]>
Co-authored-by: prklm10 <[email protected]>
Co-authored-by: Mukul Bhutiani <[email protected]>
  • Loading branch information
6 people authored and amandeepsingh333 committed Jan 10, 2025
1 parent c03faf1 commit 1dde534
Show file tree
Hide file tree
Showing 27 changed files with 271 additions and 20 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@
"@babel/eslint-parser": "^7.14.7",
"@babel/preset-env": "^7.14.7",
"@babel/register": "^7.17.7",
"babel-plugin-transform-import-meta": "^2.2.1",
"@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-babel": "^6.0.0",
"@rollup/plugin-commonjs": "^21.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"babel-plugin-istanbul": "^7.0.0",
"babel-plugin-module-resolver": "^5.0.2",
"babel-plugin-transform-import-meta": "^2.2.1",
"cross-env": "^7.0.2",
"eslint": "^7.30.0",
"eslint-config-standard": "^16.0.2",
Expand Down
3 changes: 2 additions & 1 deletion packages/cli-build/test/finalize.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ describe('percy build:finalize', () => {

it('defaults PERCY_PARALLEL_TOTAL to -1', async () => {
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';

process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
expect(process.env.PERCY_PARALLEL_TOTAL).toBeUndefined();
await finalize();
expect(process.env.PERCY_PARALLEL_TOTAL).toEqual('-1');
});

it('gets parallel build info and finalizes all parallel builds', async () => {
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
await finalize();

expect(logger.stderr).toEqual([]);
Expand Down
3 changes: 2 additions & 1 deletion packages/cli-build/test/wait.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ describe('percy build:wait', () => {

beforeEach(async () => {
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
await setupTest({ loggerTTY: true });
});

afterEach(() => {
delete process.env.PERCY_TOKEN;
delete process.env.PERCY_ENABLE;
delete process.env.PERCY_FORCE_PKG_VALUE;
});

it('does nothing and logs when percy is not enabled', async () => {
Expand Down Expand Up @@ -232,7 +234,6 @@ describe('percy build:wait', () => {
})]);

await expectAsync(wait(['--build=123'])).toBeRejected();

expect(logger.stdout).toEqual([]);
expect(logger.stderr).toEqual(jasmine.arrayContaining([
'[percy] Build #10 failed! https://percy.io/test/test/123',
Expand Down
7 changes: 7 additions & 0 deletions packages/cli-exec/test/exec.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@ import exec from '@percy/cli-exec';
describe('percy exec', () => {
beforeEach(async () => {
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
jasmine.DEFAULT_TIMEOUT_INTERVAL = 25000;
await setupTest();

let { default: which } = await import('which');
spyOn(which, 'sync').and.callFake(c => c);
spyOn(process, 'exit').and.callFake(c => c);
process.env.PERCY_CLIENT_ERROR_LOGS = false;

// Ensure global.__MOCK_IMPORTS__ is defined
global.__MOCK_IMPORTS__ = global.__MOCK_IMPORTS__ || new Map();
});

afterEach(() => {
delete process.env.PERCY_TOKEN;
delete process.env.PERCY_FORCE_PKG_VALUE;
delete process.env.PERCY_ENABLE;
delete process.env.PERCY_BUILD_ID;
delete process.env.PERCY_PARALLEL_TOTAL;
Expand Down Expand Up @@ -243,9 +248,11 @@ describe('percy exec', () => {
let [e, err] = [new EventEmitter(), new Error('spawn error')];
let crossSpawn = () => (setImmediate(() => e.emit('error', err)), e);
global.__MOCK_IMPORTS__.set('cross-spawn', { default: crossSpawn });

let stdinSpy = spyOn(process.stdin, 'pipe').and.resolveTo('some response');

await expectAsync(exec(['--', 'foobar'])).toBeRejected();

expect(stdinSpy).toHaveBeenCalled();
console.log(logger.stderr);
expect(logger.stderr).toEqual(jasmine.arrayContaining([
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-exec/test/ping.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ describe('percy exec:ping', () => {

beforeEach(async () => {
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
await setupTest();
});

afterEach(async () => {
delete process.env.PERCY_TOKEN;
delete process.env.PERCY_FORCE_PKG_VALUE;
delete process.env.PERCY_ENABLE;
delete process.env.PERCY_PARALLEL_TOTAL;
delete process.env.PERCY_PARTIAL_BUILD;
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-exec/test/start.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe('percy exec:start', () => {

beforeEach(async () => {
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
await setupTest();

started = start(['--quiet']);
Expand Down Expand Up @@ -117,6 +118,7 @@ describe('percy exec:start', () => {
logger.reset();

process.env.PERCY_ENABLE = '0';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
await start();

expect(logger.stdout).toEqual([]);
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-exec/test/stop.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ describe('percy exec:stop', () => {

beforeEach(async () => {
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
await setupTest();
});

afterEach(async () => {
delete process.env.PERCY_TOKEN;
delete process.env.PERCY_FORCE_PKG_VALUE;
delete process.env.PERCY_ENABLE;
delete process.env.PERCY_PARALLEL_TOTAL;
delete process.env.PERCY_PARTIAL_BUILD;
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-snapshot/test/directory.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ describe('percy snapshot <directory>', () => {
beforeEach(async () => {
snapshot.packageInformation = { name: '@percy/cli-snapshot' };
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });

await setupTest({
filesystem: {
Expand All @@ -21,6 +22,7 @@ describe('percy snapshot <directory>', () => {

afterEach(() => {
delete process.env.PERCY_TOKEN;
delete process.env.PERCY_FORCE_PKG_VALUE;
delete process.env.PERCY_ENABLE;
delete snapshot.packageInformation;
delete process.env.PERCY_CLIENT_ERROR_LOGS;
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-snapshot/test/file.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('percy snapshot <file>', () => {
beforeEach(async () => {
snapshot.packageInformation = { name: '@percy/cli-snapshot' };
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });

server = await createTestServer({
default: () => [200, 'text/html', '<p>Test</p>']
Expand Down Expand Up @@ -44,6 +45,7 @@ describe('percy snapshot <file>', () => {

afterEach(async () => {
delete process.env.PERCY_TOKEN;
delete process.env.PERCY_FORCE_PKG_VALUE;
delete process.env.PERCY_CLIENT_ERROR_LOGS;
delete snapshot.packageInformation;
await server.close();
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-snapshot/test/sitemap.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ describe('percy snapshot <sitemap>', () => {
beforeEach(async () => {
snapshot.packageInformation = { name: '@percy/cli-snapshot' };
process.env.PERCY_TOKEN = '<<PERCY_TOKEN>>';
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
await setupTest();

server = await createTestServer({
Expand All @@ -33,6 +34,7 @@ describe('percy snapshot <sitemap>', () => {

afterEach(async () => {
delete process.env.PERCY_TOKEN;
delete process.env.PERCY_FORCE_PKG_VALUE;
delete snapshot.packageInformation;
await server.close();
});
Expand Down
1 change: 1 addition & 0 deletions packages/cli-upload/test/upload.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('percy upload', () => {
upload.packageInformation = { name: '@percy/cli-upload' };
process.env.PERCY_TOKEN = 'web_<<PERCY_TOKEN>>';
process.env.PERCY_CLIENT_ERROR_LOGS = false;
process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
await setupTest({
filesystem: {
'images/test-1.png': pixel,
Expand Down
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"dependencies": {
"@percy/env": "1.30.6",
"@percy/logger": "1.30.6",
"pac-proxy-agent": "^7.0.2",
"pako": "^2.1.0"
}
}
6 changes: 5 additions & 1 deletion packages/client/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {

// Default client API URL can be set with an env var for API development
const { PERCY_CLIENT_API_URL = 'https://percy.io/api/v1' } = process.env;
const pkg = getPackageJSON(import.meta.url);
let pkg = getPackageJSON(import.meta.url);
// minimum polling interval milliseconds
const MIN_POLLING_INTERVAL = 1_000;
const INVALID_TOKEN_ERROR_MESSAGE = 'Unable to retrieve snapshot details with write access token. Kindly use a full access token for retrieving snapshot details with Synchronous CLI.';
Expand Down Expand Up @@ -83,6 +83,10 @@ export class PercyClient {

// Stringifies client and environment info.
userAgent() {
// forcedPkgValue has been added since when percy package is bundled inside Electron app (LCNC)
// we can't read Percy's package json for package name and version, so we are passing it via env variables
if (this.env.forcedPkgValue) pkg = this.env.forcedPkgValue;

let client = new Set([`Percy/${/\w+$/.exec(this.apiUrl)}`]
.concat(`${pkg.name}/${pkg.version}`, ...this.clientInfo)
.filter(Boolean));
Expand Down
48 changes: 43 additions & 5 deletions packages/client/src/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,28 @@ import http from 'http';
import https from 'https';
import logger from '@percy/logger';
import { stripQuotesAndSpaces } from '@percy/env/utils';
import { PacProxyAgent } from 'pac-proxy-agent';

const CRLF = '\r\n';
const STATUS_REG = /^HTTP\/1.[01] (\d*)/;

// function to create PAC proxy agent
export function createPacAgent(pacUrl, options = {}) {
pacUrl = stripQuotesAndSpaces(pacUrl);
try {
const agent = new PacProxyAgent(pacUrl, {
keepAlive: true,
...options
});

logger('client:proxy').info(`Successfully loaded PAC file from: ${pacUrl}`);
return agent;
} catch (error) {
logger('client:proxy').error(`Failed to load PAC file, error message: ${error.message}, stack: ${error.stack}`);
throw new Error(`Failed to initialize PAC proxy: ${error.message}`);
}
}

// Returns true if the URL hostname matches any patterns
export function hostnameMatches(patterns, url) {
let subject = new URL(url);
Expand Down Expand Up @@ -219,11 +237,31 @@ export function proxyAgentFor(url, options) {
let { protocol, hostname } = new URL(url);
let cachekey = `${protocol}//${hostname}`;

if (!cache.has(cachekey)) {
cache.set(cachekey, protocol === 'https:'
? new ProxyHttpsAgent(options)
: new ProxyHttpAgent(options));
// If we already have a cached agent, return it
if (cache.has(cachekey)) {
return cache.get(cachekey);
}

return cache.get(cachekey);
try {
let agent;
const pacUrl = process.env.PERCY_PAC_FILE_URL;

// If PAC URL is provided, use PAC proxy
if (pacUrl) {
logger('client:proxy').info(`Using PAC file from: ${pacUrl}`);
agent = createPacAgent(pacUrl, options);
} else {
// Fall back to other proxy configuration
agent = protocol === 'https:'
? new ProxyHttpsAgent(options)
: new ProxyHttpAgent(options);
}

// Cache the created agent
cache.set(cachekey, agent);
return agent;
} catch (error) {
logger('client:proxy').error(`Failed to create proxy agent: ${error.message}`);
throw error;
}
}
4 changes: 3 additions & 1 deletion packages/client/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ export async function request(url, options = {}, callback) {
let { protocol, hostname, port, pathname, search, hash } = new URL(url);

// reference the default export so tests can mock it
let { default: http } = await import(protocol === 'https:' ? 'https' : 'http');
// bundling cli inside electron or another package fails if we import it
// like this: await import(protocol === 'https:' ? 'https' : 'http');
let { default: http } = protocol === 'https:' ? await import('https') : await import('http');
let { proxyAgentFor } = await import('./proxy.js');

// automatically stringify body content
Expand Down
23 changes: 21 additions & 2 deletions packages/client/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,21 @@ describe('PercyClient', () => {
await logger.mock({ level: 'debug' });
await api.mock();
delete process.env.PERCY_GZIP;

process.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
client = new PercyClient({
token: 'PERCY_TOKEN'
});
});

describe('#userAgent()', () => {
it('uses default package value when env.forcedPkgValue is not set', () => {
delete process.env.PERCY_FORCE_PKG_VALUE;
client = new PercyClient({ token: 'PERCY_TOKEN' });

expect(client.userAgent()).toMatch(
/^Percy\/v1 @percy\/client\/\S+ \(node\/v[\d.]+.*\)$/
);
});
it('contains client and environment information', () => {
expect(client.userAgent()).toMatch(
/^Percy\/v1 @percy\/client\/\S+ \(node\/v[\d.]+.*\)$/
Expand All @@ -38,7 +46,7 @@ describe('PercyClient', () => {
expect(client.userAgent()).toMatch(
/^Percy\/v1 @percy\/client\/\S+ client-info \(env-info; node\/v[\d.]+.*\)$/
);
expect(logger.stderr.length).toEqual(2);
expect(logger.stderr.length).toBeGreaterThanOrEqual(2);
});

it('it logs a debug warning when no info is passed', async () => {
Expand Down Expand Up @@ -98,6 +106,17 @@ describe('PercyClient', () => {
/^Percy\/v1 @percy\/client\/\S+ client-info \(env-info; node\/v[\d.]+.*\)$/
);
});

it('uses forced package value when set', () => {
client.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
client = new PercyClient({
token: 'PERCY_TOKEN'
});
client.env.PERCY_FORCE_PKG_VALUE = JSON.stringify({ name: '@percy/client', version: '1.0.0' });
expect(client.userAgent()).toMatch(
/^Percy\/v1 @percy\/client\/1.0.0 \(node\/v[\d.]+.*\)$/
);
});
});

describe('#get()', () => {
Expand Down
Loading

0 comments on commit 1dde534

Please sign in to comment.