Add util function for parsing durations and many more tests (#69)
This commit is contained in:
parent
1e9245c68a
commit
057960bb62
110
dist/main/index.js
vendored
110
dist/main/index.js
vendored
@ -610,7 +610,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.trimmedString = exports.fromBase64 = exports.toBase64 = exports.explodeStrings = exports.removeExportedCredentials = exports.writeSecureFile = void 0;
|
exports.parseDuration = exports.trimmedString = exports.fromBase64 = exports.toBase64 = exports.explodeStrings = exports.removeExportedCredentials = exports.writeSecureFile = void 0;
|
||||||
const fs_1 = __webpack_require__(747);
|
const fs_1 = __webpack_require__(747);
|
||||||
const crypto_1 = __importDefault(__webpack_require__(417));
|
const crypto_1 = __importDefault(__webpack_require__(417));
|
||||||
const path_1 = __importDefault(__webpack_require__(622));
|
const path_1 = __importDefault(__webpack_require__(622));
|
||||||
@ -673,17 +673,38 @@ exports.removeExportedCredentials = removeExportedCredentials;
|
|||||||
* of trimmed strings.
|
* of trimmed strings.
|
||||||
*/
|
*/
|
||||||
function explodeStrings(input) {
|
function explodeStrings(input) {
|
||||||
if (input == null || input.length === 0) {
|
if (!input || input.trim().length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const list = new Array();
|
const list = new Array();
|
||||||
for (const line of input.split(`\n`)) {
|
let curr = '';
|
||||||
for (const piece of line.split(',')) {
|
let escaped = false;
|
||||||
const entry = piece.trim();
|
for (const ch of input) {
|
||||||
if (entry !== '') {
|
if (escaped) {
|
||||||
list.push(entry);
|
curr += ch;
|
||||||
}
|
escaped = false;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
switch (ch) {
|
||||||
|
case '\\':
|
||||||
|
escaped = true;
|
||||||
|
continue;
|
||||||
|
case ',':
|
||||||
|
case '\n': {
|
||||||
|
const val = curr.trim();
|
||||||
|
if (val) {
|
||||||
|
list.push(val);
|
||||||
|
}
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
curr += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const val = curr.trim();
|
||||||
|
if (val) {
|
||||||
|
list.push(val);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@ -705,7 +726,7 @@ exports.toBase64 = toBase64;
|
|||||||
*/
|
*/
|
||||||
function fromBase64(s) {
|
function fromBase64(s) {
|
||||||
const str = s.replace(/-/g, '+').replace(/_/g, '/');
|
const str = s.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
while (str.length % 4)
|
while (s.length % 4)
|
||||||
s += '=';
|
s += '=';
|
||||||
return Buffer.from(str, 'base64').toString('utf8');
|
return Buffer.from(str, 'base64').toString('utf8');
|
||||||
}
|
}
|
||||||
@ -718,6 +739,65 @@ function trimmedString(s) {
|
|||||||
return s ? s.trim() : '';
|
return s ? s.trim() : '';
|
||||||
}
|
}
|
||||||
exports.trimmedString = trimmedString;
|
exports.trimmedString = trimmedString;
|
||||||
|
/**
|
||||||
|
* parseDuration parses a user-supplied string duration with optional suffix and
|
||||||
|
* returns a number representing the number of seconds. It returns 0 when given
|
||||||
|
* the empty string.
|
||||||
|
*
|
||||||
|
* @param str Duration string
|
||||||
|
*/
|
||||||
|
function parseDuration(str) {
|
||||||
|
const given = (str || '').trim();
|
||||||
|
if (!given) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let total = 0;
|
||||||
|
let curr = '';
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const ch = str[i];
|
||||||
|
switch (ch) {
|
||||||
|
case ' ':
|
||||||
|
continue;
|
||||||
|
case ',':
|
||||||
|
continue;
|
||||||
|
case 's': {
|
||||||
|
total += +curr;
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm': {
|
||||||
|
total += +curr * 60;
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'h': {
|
||||||
|
total += +curr * 60 * 60;
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
curr += ch;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new SyntaxError(`Unsupported character "${ch}" at position ${i}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Anything left over is seconds
|
||||||
|
if (curr) {
|
||||||
|
total += +curr;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
exports.parseDuration = parseDuration;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
@ -1901,8 +1981,8 @@ class CredentialsJSONClient {
|
|||||||
const signature = signer.sign(__classPrivateFieldGet(this, _CredentialsJSONClient_credentials, "f")['private_key']);
|
const signature = signer.sign(__classPrivateFieldGet(this, _CredentialsJSONClient_credentials, "f")['private_key']);
|
||||||
return message + '.' + (0, utils_1.toBase64)(signature);
|
return message + '.' + (0, utils_1.toBase64)(signature);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (err) {
|
||||||
throw new Error(`Failed to sign auth token: ${e}`);
|
throw new Error(`Failed to sign auth token using ${this.getServiceAccount()}: ${err}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2190,8 +2270,8 @@ class BaseClient {
|
|||||||
expiration: parsed['expireTime'],
|
expiration: parsed['expireTime'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (err) {
|
||||||
throw new Error(`Failed to generate Google Cloud access token for ${serviceAccount}: ${e}`);
|
throw new Error(`Failed to generate Google Cloud access token for ${serviceAccount}: ${err}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2267,8 +2347,8 @@ class WorkloadIdentityClient {
|
|||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* getAuthToken generates a Google Cloud federated token using the provided OIDC
|
* getAuthToken generates a Google Cloud federated token using the provided
|
||||||
* token and Workload Identity Provider.
|
* OIDC token and Workload Identity Provider.
|
||||||
*/
|
*/
|
||||||
getAuthToken() {
|
getAuthToken() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
98
dist/post/index.js
vendored
98
dist/post/index.js
vendored
@ -449,7 +449,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.trimmedString = exports.fromBase64 = exports.toBase64 = exports.explodeStrings = exports.removeExportedCredentials = exports.writeSecureFile = void 0;
|
exports.parseDuration = exports.trimmedString = exports.fromBase64 = exports.toBase64 = exports.explodeStrings = exports.removeExportedCredentials = exports.writeSecureFile = void 0;
|
||||||
const fs_1 = __webpack_require__(747);
|
const fs_1 = __webpack_require__(747);
|
||||||
const crypto_1 = __importDefault(__webpack_require__(417));
|
const crypto_1 = __importDefault(__webpack_require__(417));
|
||||||
const path_1 = __importDefault(__webpack_require__(622));
|
const path_1 = __importDefault(__webpack_require__(622));
|
||||||
@ -512,17 +512,38 @@ exports.removeExportedCredentials = removeExportedCredentials;
|
|||||||
* of trimmed strings.
|
* of trimmed strings.
|
||||||
*/
|
*/
|
||||||
function explodeStrings(input) {
|
function explodeStrings(input) {
|
||||||
if (input == null || input.length === 0) {
|
if (!input || input.trim().length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const list = new Array();
|
const list = new Array();
|
||||||
for (const line of input.split(`\n`)) {
|
let curr = '';
|
||||||
for (const piece of line.split(',')) {
|
let escaped = false;
|
||||||
const entry = piece.trim();
|
for (const ch of input) {
|
||||||
if (entry !== '') {
|
if (escaped) {
|
||||||
list.push(entry);
|
curr += ch;
|
||||||
}
|
escaped = false;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
switch (ch) {
|
||||||
|
case '\\':
|
||||||
|
escaped = true;
|
||||||
|
continue;
|
||||||
|
case ',':
|
||||||
|
case '\n': {
|
||||||
|
const val = curr.trim();
|
||||||
|
if (val) {
|
||||||
|
list.push(val);
|
||||||
|
}
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
curr += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const val = curr.trim();
|
||||||
|
if (val) {
|
||||||
|
list.push(val);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@ -544,7 +565,7 @@ exports.toBase64 = toBase64;
|
|||||||
*/
|
*/
|
||||||
function fromBase64(s) {
|
function fromBase64(s) {
|
||||||
const str = s.replace(/-/g, '+').replace(/_/g, '/');
|
const str = s.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
while (str.length % 4)
|
while (s.length % 4)
|
||||||
s += '=';
|
s += '=';
|
||||||
return Buffer.from(str, 'base64').toString('utf8');
|
return Buffer.from(str, 'base64').toString('utf8');
|
||||||
}
|
}
|
||||||
@ -557,6 +578,65 @@ function trimmedString(s) {
|
|||||||
return s ? s.trim() : '';
|
return s ? s.trim() : '';
|
||||||
}
|
}
|
||||||
exports.trimmedString = trimmedString;
|
exports.trimmedString = trimmedString;
|
||||||
|
/**
|
||||||
|
* parseDuration parses a user-supplied string duration with optional suffix and
|
||||||
|
* returns a number representing the number of seconds. It returns 0 when given
|
||||||
|
* the empty string.
|
||||||
|
*
|
||||||
|
* @param str Duration string
|
||||||
|
*/
|
||||||
|
function parseDuration(str) {
|
||||||
|
const given = (str || '').trim();
|
||||||
|
if (!given) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
let total = 0;
|
||||||
|
let curr = '';
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const ch = str[i];
|
||||||
|
switch (ch) {
|
||||||
|
case ' ':
|
||||||
|
continue;
|
||||||
|
case ',':
|
||||||
|
continue;
|
||||||
|
case 's': {
|
||||||
|
total += +curr;
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm': {
|
||||||
|
total += +curr * 60;
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'h': {
|
||||||
|
total += +curr * 60 * 60;
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
curr += ch;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new SyntaxError(`Unsupported character "${ch}" at position ${i}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Anything left over is seconds
|
||||||
|
if (curr) {
|
||||||
|
total += +curr;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
exports.parseDuration = parseDuration;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
@ -138,8 +138,8 @@ export class BaseClient {
|
|||||||
accessToken: parsed['accessToken'],
|
accessToken: parsed['accessToken'],
|
||||||
expiration: parsed['expireTime'],
|
expiration: parsed['expireTime'],
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
throw new Error(`Failed to generate Google Cloud access token for ${serviceAccount}: ${e}`);
|
throw new Error(`Failed to generate Google Cloud access token for ${serviceAccount}: ${err}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,8 +96,8 @@ export class CredentialsJSONClient implements AuthClient {
|
|||||||
|
|
||||||
const signature = signer.sign(this.#credentials['private_key']);
|
const signature = signer.sign(this.#credentials['private_key']);
|
||||||
return message + '.' + toBase64(signature);
|
return message + '.' + toBase64(signature);
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
throw new Error(`Failed to sign auth token: ${e}`);
|
throw new Error(`Failed to sign auth token using ${this.getServiceAccount()}: ${err}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,8 +71,8 @@ export class WorkloadIdentityClient implements AuthClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getAuthToken generates a Google Cloud federated token using the provided OIDC
|
* getAuthToken generates a Google Cloud federated token using the provided
|
||||||
* token and Workload Identity Provider.
|
* OIDC token and Workload Identity Provider.
|
||||||
*/
|
*/
|
||||||
async getAuthToken(): Promise<string> {
|
async getAuthToken(): Promise<string> {
|
||||||
const stsURL = new URL('https://sts.googleapis.com/v1/token');
|
const stsURL = new URL('https://sts.googleapis.com/v1/token');
|
||||||
|
103
src/utils.ts
103
src/utils.ts
@ -62,19 +62,43 @@ export async function removeExportedCredentials(): Promise<string> {
|
|||||||
* of trimmed strings.
|
* of trimmed strings.
|
||||||
*/
|
*/
|
||||||
export function explodeStrings(input: string): Array<string> {
|
export function explodeStrings(input: string): Array<string> {
|
||||||
if (input == null || input.length === 0) {
|
if (!input || input.trim().length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = new Array<string>();
|
const list = new Array<string>();
|
||||||
for (const line of input.split(`\n`)) {
|
let curr = '';
|
||||||
for (const piece of line.split(',')) {
|
let escaped = false;
|
||||||
const entry = piece.trim();
|
for (const ch of input) {
|
||||||
if (entry !== '') {
|
if (escaped) {
|
||||||
list.push(entry);
|
curr += ch;
|
||||||
|
escaped = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case '\\':
|
||||||
|
escaped = true;
|
||||||
|
continue;
|
||||||
|
case ',':
|
||||||
|
case '\n': {
|
||||||
|
const val = curr.trim();
|
||||||
|
if (val) {
|
||||||
|
list.push(val);
|
||||||
|
}
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
curr += ch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const val = curr.trim();
|
||||||
|
if (val) {
|
||||||
|
list.push(val);
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +119,7 @@ export function toBase64(s: string | Buffer): string {
|
|||||||
*/
|
*/
|
||||||
export function fromBase64(s: string): string {
|
export function fromBase64(s: string): string {
|
||||||
const str = s.replace(/-/g, '+').replace(/_/g, '/');
|
const str = s.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
while (str.length % 4) s += '=';
|
while (s.length % 4) s += '=';
|
||||||
return Buffer.from(str, 'base64').toString('utf8');
|
return Buffer.from(str, 'base64').toString('utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +127,69 @@ export function fromBase64(s: string): string {
|
|||||||
* trimmedString returns a string trimmed of whitespace. If the input string is
|
* trimmedString returns a string trimmed of whitespace. If the input string is
|
||||||
* null, then it returns the empty string.
|
* null, then it returns the empty string.
|
||||||
*/
|
*/
|
||||||
export function trimmedString(s: string): string {
|
export function trimmedString(s: string | undefined | null): string {
|
||||||
return s ? s.trim() : '';
|
return s ? s.trim() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parseDuration parses a user-supplied string duration with optional suffix and
|
||||||
|
* returns a number representing the number of seconds. It returns 0 when given
|
||||||
|
* the empty string.
|
||||||
|
*
|
||||||
|
* @param str Duration string
|
||||||
|
*/
|
||||||
|
export function parseDuration(str: string): number {
|
||||||
|
const given = (str || '').trim();
|
||||||
|
if (!given) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
let curr = '';
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const ch = str[i];
|
||||||
|
switch (ch) {
|
||||||
|
case ' ':
|
||||||
|
continue;
|
||||||
|
case ',':
|
||||||
|
continue;
|
||||||
|
case 's': {
|
||||||
|
total += +curr;
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm': {
|
||||||
|
total += +curr * 60;
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'h': {
|
||||||
|
total += +curr * 60 * 60;
|
||||||
|
curr = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
curr += ch;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new SyntaxError(`Unsupported character "${ch}" at position ${i}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Anything left over is seconds
|
||||||
|
if (curr) {
|
||||||
|
total += +curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
@ -4,12 +4,36 @@ import 'mocha';
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
|
||||||
import { tmpdir } from 'os';
|
import { tmpdir } from 'os';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync, readFileSync } from 'fs';
|
||||||
|
|
||||||
import { removeExportedCredentials } from '../src/utils';
|
import {
|
||||||
import { writeSecureFile } from '../src/utils';
|
explodeStrings,
|
||||||
|
fromBase64,
|
||||||
|
parseDuration,
|
||||||
|
removeExportedCredentials,
|
||||||
|
toBase64,
|
||||||
|
trimmedString,
|
||||||
|
writeSecureFile,
|
||||||
|
} from '../src/utils';
|
||||||
|
|
||||||
|
describe('Utils', () => {
|
||||||
|
describe('#writeSecureFile', () => {
|
||||||
|
it('writes data to the file', async () => {
|
||||||
|
const tmp = tmpdir();
|
||||||
|
const filePath = await writeSecureFile(tmp, 'hi');
|
||||||
|
expect(existsSync(filePath)).to.be.true;
|
||||||
|
expect(readFileSync(filePath).toString('utf8')).to.eq('hi');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generates a random name', async () => {
|
||||||
|
const tmp = tmpdir();
|
||||||
|
const filePath1 = await writeSecureFile(tmp, 'hi');
|
||||||
|
const filePath2 = await writeSecureFile(tmp, 'bye');
|
||||||
|
expect(filePath1).to.not.eq(filePath2);
|
||||||
|
expect(filePath1).to.not.eq(filePath2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('post', () => {
|
|
||||||
describe('#removeExportedCredentials', () => {
|
describe('#removeExportedCredentials', () => {
|
||||||
it('does nothing when GOOGLE_GHA_CREDS_PATH is unset', async () => {
|
it('does nothing when GOOGLE_GHA_CREDS_PATH is unset', async () => {
|
||||||
delete process.env.GOOGLE_GHA_CREDS_PATH;
|
delete process.env.GOOGLE_GHA_CREDS_PATH;
|
||||||
@ -32,4 +56,196 @@ describe('post', () => {
|
|||||||
expect(pth).to.eq('');
|
expect(pth).to.eq('');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#explodeStrings', () => {
|
||||||
|
const cases = [
|
||||||
|
{
|
||||||
|
name: 'empty string',
|
||||||
|
input: '',
|
||||||
|
expected: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'padded empty string',
|
||||||
|
input: ' ',
|
||||||
|
expected: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comma-separated',
|
||||||
|
input: 'hello , world , and goodbye',
|
||||||
|
expected: ['hello', 'world', 'and goodbye'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'newline-separated',
|
||||||
|
input: `
|
||||||
|
hello
|
||||||
|
world
|
||||||
|
and goodbye`,
|
||||||
|
expected: ['hello', 'world', 'and goodbye'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comma and newline-separated',
|
||||||
|
input: `
|
||||||
|
hello,
|
||||||
|
world,
|
||||||
|
and goodbye,`,
|
||||||
|
expected: ['hello', 'world', 'and goodbye'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'comma-escaped',
|
||||||
|
input: 'hello , world , and\\, goodbye',
|
||||||
|
expected: ['hello', 'world', 'and, goodbye'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
cases.forEach((tc) => {
|
||||||
|
it(tc.name, async () => {
|
||||||
|
expect(explodeStrings(tc.input)).to.eql(tc.expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#toBase64', () => {
|
||||||
|
const cases = [
|
||||||
|
{
|
||||||
|
name: 'empty string',
|
||||||
|
input: '',
|
||||||
|
expected: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'empty buffer',
|
||||||
|
input: Buffer.from(''),
|
||||||
|
expected: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'encodes string',
|
||||||
|
input: 'hello',
|
||||||
|
expected: 'aGVsbG8',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'encodes buffer',
|
||||||
|
input: Buffer.from('hello'),
|
||||||
|
expected: 'aGVsbG8',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
cases.forEach((tc) => {
|
||||||
|
it(tc.name, async () => {
|
||||||
|
expect(toBase64(tc.input)).to.eq(tc.expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#fromBase64', () => {
|
||||||
|
const cases = [
|
||||||
|
{
|
||||||
|
name: 'decodes',
|
||||||
|
input: 'aGVsbG8',
|
||||||
|
expected: 'hello',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'decodes padded',
|
||||||
|
input: 'aGVsbG8==',
|
||||||
|
expected: 'hello',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
cases.forEach((tc) => {
|
||||||
|
it(tc.name, async () => {
|
||||||
|
expect(fromBase64(tc.input)).to.eq(tc.expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#trimmedString', () => {
|
||||||
|
const cases = [
|
||||||
|
{
|
||||||
|
name: 'null',
|
||||||
|
input: null,
|
||||||
|
expected: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'undefined',
|
||||||
|
input: undefined,
|
||||||
|
expected: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'empty string',
|
||||||
|
input: '',
|
||||||
|
expected: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trims',
|
||||||
|
input: ' hello world ',
|
||||||
|
expected: 'hello world',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
cases.forEach((tc) => {
|
||||||
|
it(tc.name, async () => {
|
||||||
|
expect(trimmedString(tc.input)).to.eq(tc.expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#parseDuration', () => {
|
||||||
|
const cases = [
|
||||||
|
{
|
||||||
|
name: 'empty string',
|
||||||
|
input: '',
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'unitless',
|
||||||
|
input: '149585',
|
||||||
|
expected: 149585,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'with commas',
|
||||||
|
input: '149,585',
|
||||||
|
expected: 149585,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'suffix seconds',
|
||||||
|
input: '149585s',
|
||||||
|
expected: 149585,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'suffix minutes',
|
||||||
|
input: '25m',
|
||||||
|
expected: 1500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'suffix hours',
|
||||||
|
input: '12h',
|
||||||
|
expected: 43200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'suffix hours minutes seconds',
|
||||||
|
input: '12h10m55s',
|
||||||
|
expected: 43855,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'commas and spaces',
|
||||||
|
input: '12h, 10m 55s',
|
||||||
|
expected: 43855,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'invalid',
|
||||||
|
input: '12h blueberries',
|
||||||
|
error: 'Unsupported character "b" at position 4',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
cases.forEach((tc) => {
|
||||||
|
it(tc.name, async () => {
|
||||||
|
if (tc.expected) {
|
||||||
|
expect(parseDuration(tc.input)).to.eq(tc.expected);
|
||||||
|
} else if (tc.error) {
|
||||||
|
expect(() => {
|
||||||
|
parseDuration(tc.input);
|
||||||
|
}).to.throw(tc.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user