From 1857f3620d7b57f6ec6c60bbe6208d7f2ca390c9 Mon Sep 17 00:00:00 2001 From: ghe Date: Thu, 10 Sep 2020 17:04:36 +0100 Subject: [PATCH 1/4] feat: introduce handlebars & views --- .nvmrc | 2 +- README.md | 21 ++++++- package.json | 22 +++++-- src/cmds/generate.ts | 62 +++++++++++++++++++ src/cmds/json.ts | 4 +- src/lib/api/org/dependencies.ts | 2 +- src/lib/api/org/licenses.ts | 2 +- src/lib/generate-org-license-report.ts | 31 +++------- src/lib/generate-output/html/index.ts | 62 +++++++++++++++++++ .../html/templates/inline-css.hbs | 2 + .../html/templates/licenses-view.hbs | 20 ++++++ .../templates/project-dependencies-view.hbs | 0 src/lib/generate-output/index.ts | 2 + src/lib/generate-output/pdf/index.ts | 5 ++ src/lib/license-text/non-spdx.ts | 2 +- src/lib/license-text/spdx.ts | 2 +- test/lib/cmds/generate.test.ts | 17 +++++ test/lib/cmds/json.test.ts | 15 +++++ test/lib/generate-license-report-data.test.ts | 2 + test/lib/get-dependencies-data.test.ts | 1 + test/lib/get-license-data.test.ts | 2 + tsconfig.json | 2 +- 22 files changed, 243 insertions(+), 37 deletions(-) create mode 100644 src/lib/generate-output/html/index.ts create mode 100644 src/lib/generate-output/html/templates/inline-css.hbs create mode 100644 src/lib/generate-output/html/templates/licenses-view.hbs create mode 100644 src/lib/generate-output/html/templates/project-dependencies-view.hbs create mode 100644 src/lib/generate-output/index.ts create mode 100644 src/lib/generate-output/pdf/index.ts create mode 100644 test/lib/cmds/generate.test.ts create mode 100644 test/lib/cmds/json.test.ts diff --git a/.nvmrc b/.nvmrc index 91d73a1..7a3707e 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1,2 +1,2 @@ -12 +14 package-lock=false diff --git a/README.md b/README.md index d15e23c..cb4815b 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,23 @@ Snyk helps you find, fix and monitor for known vulnerabilities in your dependencies, both on an ad hoc basis and as part of your CI (Build) system. ## Snyk snyk-licenses-texts -Snyk Licenses Texts +Snyk Licenses Report that provides Organization level licenses used, copyrights & dependencies data (including license texts & their urls). + +## Usage +Ensure `SNYK_TOKEN` is set and has access to the Organization you want to generate the report for. + +### Basic CLI commands +- `help` - show help & all available commands and their options +- `json` - generate the raw JSON licenses & dependencies data +- `generate` - generates an HTML report of licenses & dependencies data + +Example usage: +`snyk-licenses-report help` +`snyk-licenses-report json --orgPublicId=` +`snyk-licenses-report generate --orgPublicId=` + + +## Development setup +- `npm i` +- `npm run test` +- `DEBUG=snyk-license* node dist/index.js generate --orgPublicId=` diff --git a/package.json b/package.json index de7f220..32c45ea 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,15 @@ "test:unit": "jest", "test:coverage": "npm run test:unit -- --coverage", "test:watch": "tsc-watch --onSuccess 'npm run test:unit'", - "build": "tsc", - "build-watch": "tsc -w", + "build": "tsc && npm run copy:templates", + "build-watch": "tsc -w && npm run copy:templates", "prepare": "npm run build", "snyk-test": "snyk test", - "pkg-binaries": "npx pkg . -t node12-linux-x64,node12-macos-x64,node12-win-x64 --out-path ./dist/binaries" + "pkg-binaries": "npx pkg . -t node12-linux-x64,node12-macos-x64,node12-win-x64 --out-path ./dist/binaries", + "copy:templates": "cpx 'src/**/*.hbs' ./dist" + }, + "bin": { + "snyk-licenses-report": "dist/index.js" }, "types": "./dist/index.d.ts", "repository": { @@ -25,11 +29,12 @@ "author": "Snyk Tech Services", "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=14" }, "files": [ "bin", - "dist" + "dist", + "src/lib/generate-output/html/templates" ], "homepage": "https://github.com/snyk-tech-services/snyk-licenses-texts#readme", "dependencies": { @@ -44,9 +49,11 @@ "snyk-config": "^3.0.0", "source-map-support": "^0.5.16", "tslib": "2.0.1", - "yargs": "16.0.2" + "yargs": "16.0.2", + "yargs-command-config": "1.0.5" }, "devDependencies": { + "cpx": "1.5.0", "@types/jest": "26.0.13", "@types/lodash": "4.14.161", "@types/node": "14.6.3", @@ -64,6 +71,9 @@ "pkg": { "scripts": [ "dist/**/*.js" + ], + "assets": [ + "src/**/templates/*.hbs" ] }, "release": { diff --git a/src/cmds/generate.ts b/src/cmds/generate.ts index e69de29..865df48 100644 --- a/src/cmds/generate.ts +++ b/src/cmds/generate.ts @@ -0,0 +1,62 @@ +import * as debugLib from 'debug'; +import { getApiToken } from '../lib/get-api-token'; +import { LicenseReportData, generateLicenseData } from '../lib/generate-org-license-report'; +import { generateHtmlReport, generatePdfReport, SupportedViews } from '../lib/generate-output'; +const debug = debugLib('snyk-licenses:generate'); + +const outputHandlers = { + [OutputFormat.HTML]: generateHtmlReport, + // [OutputFormat.PDF]: generatePdfReport +}; +const enum OutputFormat { + HTML = 'html', + // TODO: support later + // PDF = 'pdf', +} + +export const desc = + 'Generate org licenses & dependencies report in HTML format'; +export const builder = { + orgPublicId: { + required: true, + default: undefined, + desc: 'Public id of the organization in Snyk (available on organization settings)' + }, + template: { + default: undefined, + desc: 'Path to custom Handelbars.js template file (*.hbs)' + }, + outputFormat: { + default: OutputFormat.HTML, + desc: 'Report format', + options: [OutputFormat.HTML] + }, + view: { + default: SupportedViews.ORG_LICENSES, + desc: 'How should the data be represented. Defaults to a license based view.', + }, +}; +export const aliases = ['g']; + +export async function handler(argv: { + orgPublicId: string; + outputFormat: OutputFormat; + template: string; + view: SupportedViews; +}) { + try { + const { orgPublicId, outputFormat, template, view } = argv; + debug( + 'Options: ' + + JSON.stringify({ orgPublicId, outputFormat, template, view }), + ); + getApiToken(); + const licenseData: LicenseReportData = await generateLicenseData( + orgPublicId, + ); + const generateReportFunc = outputHandlers[outputFormat]; + return await generateReportFunc(licenseData, template, view); + } catch (e) { + console.error(e); + } +} diff --git a/src/cmds/json.ts b/src/cmds/json.ts index d222446..44252df 100644 --- a/src/cmds/json.ts +++ b/src/cmds/json.ts @@ -1,5 +1,5 @@ import { getApiToken } from '../lib/get-api-token'; -import { generateOrgLicensesReport } from '../lib/generate-org-license-report'; +import { generateLicenseData } from '../lib/generate-org-license-report'; export const command = 'json'; export const desc = 'Generate org licenses & dependencies data in JSON format'; @@ -17,7 +17,7 @@ export async function handler(argv: { orgPublicId: string }) { getApiToken(); // TODO: define and pass options to help filter the response // based on filters available in API - const data = await generateOrgLicensesReport(argv.orgPublicId, {}); + const data = await generateLicenseData(argv.orgPublicId, {}); console.log(JSON.stringify(data)); } catch (e) { console.error(e); diff --git a/src/lib/api/org/dependencies.ts b/src/lib/api/org/dependencies.ts index 5d33445..a54ab2d 100644 --- a/src/lib/api/org/dependencies.ts +++ b/src/lib/api/org/dependencies.ts @@ -4,7 +4,7 @@ import * as snykApiSdk from 'snyk-api-ts-client'; import { getApiToken } from '../../get-api-token'; import { SortBy, Order } from './types'; -const debug = debugLib('getDependenciesDataForOrg'); +const debug = debugLib('snyk-licenses:getDependenciesDataForOrg'); interface GetDependenciesDataOptions { sortBy: SortBy; diff --git a/src/lib/api/org/licenses.ts b/src/lib/api/org/licenses.ts index e945a6c..80a298c 100644 --- a/src/lib/api/org/licenses.ts +++ b/src/lib/api/org/licenses.ts @@ -4,7 +4,7 @@ import * as snykApiSdk from 'snyk-api-ts-client'; import { getApiToken } from '../../get-api-token'; import { SortBy, Order } from './types'; -const debug = debugLib('getLicenseDataForOrg'); +const debug = debugLib('snyk-licenses:getLicenseDataForOrg'); interface GetLicenseDataOptions { sortBy: SortBy; diff --git a/src/lib/generate-org-license-report.ts b/src/lib/generate-org-license-report.ts index e9365ad..c3a56a8 100644 --- a/src/lib/generate-org-license-report.ts +++ b/src/lib/generate-org-license-report.ts @@ -7,39 +7,26 @@ import { getLicenseDataForOrg, getDependenciesDataForOrg } from './api/org'; import { fetchSpdxLicenseTextAndUrl, fetchNonSpdxLicenseTextAndUrl } from './license-text'; import { LicenseReportDataEntry, EnrichedDependency, Dependency } from './types'; -const debug = debugLib('generateOrgLicensesReport'); +const debug = debugLib('snyk-licenses:generateOrgLicensesReport'); -interface LicenseReportData { +export interface LicenseReportData { [licenseID: string]: LicenseReportDataEntry; } -export async function generateOrgLicensesReport( - orgPublicId: string, - options, -): Promise { - try { - const licenseData: LicenseReportData = await generateLicenseData( - orgPublicId, - options, - ); - const report = licenseData; - return report as any; - } catch (e) { - debug('Failed to generate report data', e); - throw e; - } -} - export async function generateLicenseData( orgPublicId: string, - options, + options?, ): Promise { + debug(`Generating license data for Org:${orgPublicId}`); + try { const licenseData = await getLicenseDataForOrg(orgPublicId, options); + debug(`Got license API data for Org:${orgPublicId}`); const dependenciesDataRaw = await getDependenciesDataForOrg( orgPublicId, options, ); + debug(`Got dependencies API data for Org:${orgPublicId}`); const licenseReportData: LicenseReportData = {}; const dependenciesData = _.groupBy(dependenciesDataRaw.results, 'id'); // TODO: what if 0? @@ -102,12 +89,12 @@ async function getLicenseTextAndUrl( try { return await fetchSpdxLicenseTextAndUrl(id); } catch (e) { - debug('Failed to get license data as SPDX, trying non-SPDX'); + debug(`Failed to get license data for as SPDX, trying non-SPDX: ${id}`); } try { return await fetchNonSpdxLicenseTextAndUrl(id); } catch (e) { - debug('Failed to get license data as non-SPDX'); + debug(`Failed to get license data as non-SPDX: ${id}`); } return undefined; diff --git a/src/lib/generate-output/html/index.ts b/src/lib/generate-output/html/index.ts new file mode 100644 index 0000000..95cb8f2 --- /dev/null +++ b/src/lib/generate-output/html/index.ts @@ -0,0 +1,62 @@ +import * as Handlebars from 'handlebars'; +import * as path from 'path'; +import * as fs from 'fs'; +import { LicenseReportData } from '../../generate-org-license-report'; + +export const enum SupportedViews { + ORG_LICENSES = 'org-licenses', + // TODO: support later + // PROJECT_DEPENDENCIES = 'project-dependencies', +} + +export async function generateHtmlReport( + data: LicenseReportData, + templateOverridePath: string, + view: SupportedViews, +) { + // TODO: add any helpers & data transformations that are useful here + + const hbsTemplate = selectTemplate(view, templateOverridePath); + await registerPeerPartial(hbsTemplate, 'inline-css'); + const htmlTemplate = await compileTemplate(hbsTemplate); + return htmlTemplate({hello: 1}); +} + +function selectTemplate(view, templateOverride): string { + const DEFAULT_TEMPLATE = './templates/licenses-view.hbs'; + switch (view) { + case SupportedViews.ORG_LICENSES: + return templateOverride || DEFAULT_TEMPLATE; + // TODO: support later + // case SupportedViews.PROJECT_DEPENDENCIES: + // return templateOverride || '../templates/project-dependencies-view.hbs'; + default: + return DEFAULT_TEMPLATE; + } +} + +async function registerPeerPartial( + templatePath: string, + name: string, +): Promise { + const file = path.join(__dirname, templatePath); + const template = await compileTemplate(file); + Handlebars.registerPartial(name, template); +} + +async function compileTemplate( + fileName: string, +): Promise { + return readFile(path.resolve(__dirname, fileName), 'utf8').then(Handlebars.compile); +} + +function readFile(filePath: string, encoding: string): Promise { + return new Promise((resolve, reject) => { + fs.readFile(filePath, encoding, (err, data) => { + if (err) { + reject(err); + } + resolve(data); + }); + }); +} diff --git a/src/lib/generate-output/html/templates/inline-css.hbs b/src/lib/generate-output/html/templates/inline-css.hbs new file mode 100644 index 0000000..d498387 --- /dev/null +++ b/src/lib/generate-output/html/templates/inline-css.hbs @@ -0,0 +1,2 @@ + diff --git a/src/lib/generate-output/html/templates/licenses-view.hbs b/src/lib/generate-output/html/templates/licenses-view.hbs new file mode 100644 index 0000000..2e9c2e6 --- /dev/null +++ b/src/lib/generate-output/html/templates/licenses-view.hbs @@ -0,0 +1,20 @@ + + + + + + + + + Snyk Licenses Report + + + {{> inline-css }} + + + +
+ {{data}} +
+ diff --git a/src/lib/generate-output/html/templates/project-dependencies-view.hbs b/src/lib/generate-output/html/templates/project-dependencies-view.hbs new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/generate-output/index.ts b/src/lib/generate-output/index.ts new file mode 100644 index 0000000..0c174fb --- /dev/null +++ b/src/lib/generate-output/index.ts @@ -0,0 +1,2 @@ +export * from './html'; +export * from './pdf'; diff --git a/src/lib/generate-output/pdf/index.ts b/src/lib/generate-output/pdf/index.ts new file mode 100644 index 0000000..b490a00 --- /dev/null +++ b/src/lib/generate-output/pdf/index.ts @@ -0,0 +1,5 @@ +export function generatePdfReport() { + // TODO: find a package that could convert the HTML to pdf + // or where we could use the same handlebars template & css + // to generate the pdf +} diff --git a/src/lib/license-text/non-spdx.ts b/src/lib/license-text/non-spdx.ts index d92a3f0..eb7bac2 100644 --- a/src/lib/license-text/non-spdx.ts +++ b/src/lib/license-text/non-spdx.ts @@ -6,7 +6,7 @@ import * as path from 'path'; export async function fetchNonSpdxLicenseTextAndUrl( licenseId: string, ): Promise<{ licenseText: string; licenseUrl: string }> { - const debug = debugLib('fetchNonSpdxLicenseText'); + const debug = debugLib('snyk-licenses:fetchNonSpdxLicenseText'); const fileName = `licenses/${licenseId}.html`; try { const licenseText = await fs.readFileSync( diff --git a/src/lib/license-text/spdx.ts b/src/lib/license-text/spdx.ts index c35369e..4d511de 100644 --- a/src/lib/license-text/spdx.ts +++ b/src/lib/license-text/spdx.ts @@ -6,7 +6,7 @@ import * as cheerio from 'cheerio'; export async function fetchSpdxLicenseTextAndUrl( licenseId: string, ): Promise<{ licenseText: string; licenseUrl: string }> { - const debug = debugLib('fetchSpdxLicenseText'); + const debug = debugLib('snyk-licenses:fetchSpdxLicenseText'); const licenseUrl = `https://spdx.org/licenses/${licenseId}.html`; try { const res = await fetch(licenseUrl); diff --git a/test/lib/cmds/generate.test.ts b/test/lib/cmds/generate.test.ts new file mode 100644 index 0000000..8199410 --- /dev/null +++ b/test/lib/cmds/generate.test.ts @@ -0,0 +1,17 @@ +describe('snyk-licenses-report generate', () => { + const OLD_ENV = process.env; + process.env.SNYK_TOKEN = process.env.SNYK_TEST_TOKEN; + const ORG_ID = process.env.TEST_ORG_ID as string; + + afterAll(async () => { + process.env = { ...OLD_ENV }; + }); + test('SNYK_TOKEN & ORG_ID are set', async () => { + expect(process.env.SNYK_TOKEN).not.toBeNull(); + expect(process.env.ORG_ID).not.toBeNull(); + }); + test.todo('Test running CLI with generate command + missing Org ID'); + + test.todo('Test running CLI with generate command, for happy path'); + test.todo('Test running CLI with generate command, for when API fails aka bad org id provided'); +}); diff --git a/test/lib/cmds/json.test.ts b/test/lib/cmds/json.test.ts new file mode 100644 index 0000000..d22d10e --- /dev/null +++ b/test/lib/cmds/json.test.ts @@ -0,0 +1,15 @@ +describe('snyk-licenses-report json', () => { + const OLD_ENV = process.env; + process.env.SNYK_TOKEN = process.env.SNYK_TEST_TOKEN; + const ORG_ID = process.env.TEST_ORG_ID as string; + + afterAll(async () => { + process.env = { ...OLD_ENV }; + }); + test('SNYK_TOKEN & ORG_ID are set', async () => { + expect(process.env.SNYK_TOKEN).not.toBeNull(); + expect(process.env.ORG_ID).not.toBeNull(); + }); + test.todo('Test running CLI with json command, for happy path'); + test.todo('Test running CLI with json command, for when API fails aka bad org id provided'); +}); diff --git a/test/lib/generate-license-report-data.test.ts b/test/lib/generate-license-report-data.test.ts index d514bbf..6322b0f 100644 --- a/test/lib/generate-license-report-data.test.ts +++ b/test/lib/generate-license-report-data.test.ts @@ -24,4 +24,6 @@ describe('Get org licenses', () => { expect(licenseRes['Unlicense'].dependencies[0].issuesMedium).not.toBeNull(); expect(licenseRes['Unlicense'].dependencies[0].latestVersion).not.toBeNull(); }, 50000); + + test.todo('Test for when API fails aka bad org id provided'); }); diff --git a/test/lib/get-dependencies-data.test.ts b/test/lib/get-dependencies-data.test.ts index 5fd79f5..a6c5787 100644 --- a/test/lib/get-dependencies-data.test.ts +++ b/test/lib/get-dependencies-data.test.ts @@ -16,4 +16,5 @@ describe('Get org dependencies', () => { const licenseRes = await getDependenciesDataForOrg(ORG_ID); expect(licenseRes.results.length > 0).toBeTruthy(); }, 50000); + test.todo('Test for when API fails aka bad org id provided'); }); diff --git a/test/lib/get-license-data.test.ts b/test/lib/get-license-data.test.ts index ea03912..a6fcf1c 100644 --- a/test/lib/get-license-data.test.ts +++ b/test/lib/get-license-data.test.ts @@ -16,4 +16,6 @@ describe('Get org licenses', () => { const licenseRes = await getDependenciesDataForOrg(ORG_ID); expect(licenseRes.results.length > 0).toBeTruthy(); }, 50000); + + test.todo('Test for when API fails aka bad org id provided'); }); diff --git a/tsconfig.json b/tsconfig.json index 55bc439..d749e57 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,5 +6,5 @@ "sourceMap": true, "declaration": true }, - "include": ["./src/lib/**/*", "./src/**/*", "./src/cmds"] + "include": ["./src/**/**/*"] } From 3da3f9fa5be81caa33dc27871c0e29cf8697c156 Mon Sep 17 00:00:00 2001 From: ghe Date: Mon, 28 Sep 2020 16:11:45 +0100 Subject: [PATCH 2/4] test: basic system tests for CLI commands --- test/lib/cmds/generate.test.ts | 17 -------------- test/lib/cmds/json.test.ts | 15 ------------- test/system/__snapshots__/basic.test.ts.snap | 15 +++++++++++++ .../__snapshots__/generate.test.ts.snap | 20 +++++++++++++++++ test/system/__snapshots__/json.test.ts.snap | 15 +++++++++++++ test/system/basic.test.ts | 16 ++++++++++++++ test/system/generate.test.ts | 22 +++++++++++++++++++ test/system/json.test.ts | 21 ++++++++++++++++++ 8 files changed, 109 insertions(+), 32 deletions(-) delete mode 100644 test/lib/cmds/generate.test.ts delete mode 100644 test/lib/cmds/json.test.ts create mode 100644 test/system/__snapshots__/basic.test.ts.snap create mode 100644 test/system/__snapshots__/generate.test.ts.snap create mode 100644 test/system/__snapshots__/json.test.ts.snap create mode 100644 test/system/basic.test.ts create mode 100644 test/system/generate.test.ts create mode 100644 test/system/json.test.ts diff --git a/test/lib/cmds/generate.test.ts b/test/lib/cmds/generate.test.ts deleted file mode 100644 index 8199410..0000000 --- a/test/lib/cmds/generate.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -describe('snyk-licenses-report generate', () => { - const OLD_ENV = process.env; - process.env.SNYK_TOKEN = process.env.SNYK_TEST_TOKEN; - const ORG_ID = process.env.TEST_ORG_ID as string; - - afterAll(async () => { - process.env = { ...OLD_ENV }; - }); - test('SNYK_TOKEN & ORG_ID are set', async () => { - expect(process.env.SNYK_TOKEN).not.toBeNull(); - expect(process.env.ORG_ID).not.toBeNull(); - }); - test.todo('Test running CLI with generate command + missing Org ID'); - - test.todo('Test running CLI with generate command, for happy path'); - test.todo('Test running CLI with generate command, for when API fails aka bad org id provided'); -}); diff --git a/test/lib/cmds/json.test.ts b/test/lib/cmds/json.test.ts deleted file mode 100644 index d22d10e..0000000 --- a/test/lib/cmds/json.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -describe('snyk-licenses-report json', () => { - const OLD_ENV = process.env; - process.env.SNYK_TOKEN = process.env.SNYK_TEST_TOKEN; - const ORG_ID = process.env.TEST_ORG_ID as string; - - afterAll(async () => { - process.env = { ...OLD_ENV }; - }); - test('SNYK_TOKEN & ORG_ID are set', async () => { - expect(process.env.SNYK_TOKEN).not.toBeNull(); - expect(process.env.ORG_ID).not.toBeNull(); - }); - test.todo('Test running CLI with json command, for happy path'); - test.todo('Test running CLI with json command, for when API fails aka bad org id provided'); -}); diff --git a/test/system/__snapshots__/basic.test.ts.snap b/test/system/__snapshots__/basic.test.ts.snap new file mode 100644 index 0000000..aa28c42 --- /dev/null +++ b/test/system/__snapshots__/basic.test.ts.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`\`snyk-licenses-report help <...>\` Shows help text as expected 1`] = ` +"index.js + +Commands: + index.js generate Generate org licenses & dependencies report in HTML format + [aliases: g] + index.js json Generate org licenses & dependencies data in JSON format + [aliases: j] + +Options: + --version Show version number [boolean] + --help Show help [boolean]" +`; diff --git a/test/system/__snapshots__/generate.test.ts.snap b/test/system/__snapshots__/generate.test.ts.snap new file mode 100644 index 0000000..367ca9a --- /dev/null +++ b/test/system/__snapshots__/generate.test.ts.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`\`snyk-licenses-report generate <...>\` Shows error when missing --orgPublicId 1`] = ` +"Command failed: node ./dist/index.js generate +index.js generate + +Generate org licenses & dependencies report in HTML format + +Options: + --version Show version number [boolean] + --help Show help [boolean] + --orgPublicId Public id of the organization in Snyk (available on + organization settings) [required] + --template Path to custom Handelbars.js template file (*.hbs) + --outputFormat Report format [default: \\"html\\"] + --view How should the data be represented. Defaults to a license + based view. [default: \\"org-licenses\\"] + +Missing required argument: orgPublicId" +`; diff --git a/test/system/__snapshots__/json.test.ts.snap b/test/system/__snapshots__/json.test.ts.snap new file mode 100644 index 0000000..6270fd1 --- /dev/null +++ b/test/system/__snapshots__/json.test.ts.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`\`snyk-licenses-report json <...>\` Shows error when missing --orgPublicId 1`] = ` +"Command failed: node ./dist/index.js json +index.js json + +Generate org licenses & dependencies data in JSON format + +Options: + --version Show version number [boolean] + --help Show help [boolean] + --orgPublicId [required] + +Missing required argument: orgPublicId" +`; diff --git a/test/system/basic.test.ts b/test/system/basic.test.ts new file mode 100644 index 0000000..06ec6a7 --- /dev/null +++ b/test/system/basic.test.ts @@ -0,0 +1,16 @@ +import { exec } from 'child_process'; +import { sep } from 'path'; +const main = './dist/index.js'.replace(/\//g, sep); + +describe('`snyk-licenses-report help <...>`', () => { + it('Shows help text as expected', async (done) => { + return exec(`node ${main} help`, (err, stdout) => { + if (err) { + throw err; + } + expect(err).toBeNull(); + expect(stdout.trim()).toMatchSnapshot(); + done(); + }); + }); +}); diff --git a/test/system/generate.test.ts b/test/system/generate.test.ts new file mode 100644 index 0000000..ea7d3d3 --- /dev/null +++ b/test/system/generate.test.ts @@ -0,0 +1,22 @@ +import { exec } from 'child_process'; +import { sep } from 'path'; +const main = './dist/index.js'.replace(/\//g, sep); + +const ORG_ID = process.env.TEST_ORG_ID as string; + +describe('`snyk-licenses-report generate <...>`', () => { + it('Shows error when missing --orgPublicId', async (done) => { + exec(`node ${main} generate`, (err, stdout) => { + expect(stdout).toBe(""); + expect(err.message.trim()).toMatchSnapshot(); + done(); + }); + }); + + it.todo("generated the report successfully with default params"); + it.todo("generated the report successfully with custom template") + it.todo("generated the report successfully with custom template") + + it.todo("API is down"); + it.todo("Requested org has no licenses policy"); +}); diff --git a/test/system/json.test.ts b/test/system/json.test.ts new file mode 100644 index 0000000..61dff7d --- /dev/null +++ b/test/system/json.test.ts @@ -0,0 +1,21 @@ +import { exec } from 'child_process'; +import { sep } from 'path'; +const main = './dist/index.js'.replace(/\//g, sep); +const ORG_ID = process.env.TEST_ORG_ID as string; +describe('`snyk-licenses-report json <...>`', () => { + it('Shows error when missing --orgPublicId', async (done) => { + exec(`node ${main} json`, (err, stdout) => { + expect(stdout).toBe(""); + expect(err.message.trim()).toMatchSnapshot(); + done(); + }); + }); + it('Generated JSON data with correct --orgPublicId', async (done) => { + exec(`node ${main} json --orgPublicId=${ORG_ID}`, (err, stdout) => { + expect(err).toBeNull(); + console.log({err, stdout}) + expect(stdout.trim()).toMatch("BSD-2-Clause"); + done(); + }); + }, 30000); +}); From 4caa90139b9dd420415a22e0316f45cf300acc66 Mon Sep 17 00:00:00 2001 From: ghe Date: Mon, 28 Sep 2020 16:54:49 +0100 Subject: [PATCH 3/4] feat: enhance debug messages --- src/cmds/generate.ts | 4 +++- src/cmds/json.ts | 20 ++++++++++++++++++-- src/lib/api/org/dependencies.ts | 2 +- src/lib/api/org/licenses.ts | 2 +- src/lib/generate-org-license-report.ts | 16 +++++++++------- src/lib/generate-output/html/index.ts | 4 ++++ test/system/json.test.ts | 4 ++-- 7 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/cmds/generate.ts b/src/cmds/generate.ts index 865df48..56c3903 100644 --- a/src/cmds/generate.ts +++ b/src/cmds/generate.ts @@ -29,9 +29,11 @@ export const builder = { outputFormat: { default: OutputFormat.HTML, desc: 'Report format', + // TODO: add also PDF when ready options: [OutputFormat.HTML] }, view: { + // TODO: add also dependency based view when ready default: SupportedViews.ORG_LICENSES, desc: 'How should the data be represented. Defaults to a license based view.', }, @@ -47,7 +49,7 @@ export async function handler(argv: { try { const { orgPublicId, outputFormat, template, view } = argv; debug( - 'Options: ' + + 'ℹ️ Options: ' + JSON.stringify({ orgPublicId, outputFormat, template, view }), ); getApiToken(); diff --git a/src/cmds/json.ts b/src/cmds/json.ts index 44252df..fe64052 100644 --- a/src/cmds/json.ts +++ b/src/cmds/json.ts @@ -1,5 +1,10 @@ +import * as debugLib from 'debug'; + import { getApiToken } from '../lib/get-api-token'; import { generateLicenseData } from '../lib/generate-org-license-report'; +import { SupportedViews } from '../lib/generate-output'; + +const debug = debugLib('snyk-licenses:json'); export const command = 'json'; export const desc = 'Generate org licenses & dependencies data in JSON format'; @@ -8,16 +13,27 @@ export const builder = { required: true, default: undefined, }, + view: { + // TODO: add also dependency based view when ready + default: SupportedViews.ORG_LICENSES, + desc: + 'How should the data be represented. Defaults to a license based view.', + }, }; export const aliases = ['j']; -export async function handler(argv: { orgPublicId: string }) { +export async function handler(argv: { + orgPublicId: string; + view: SupportedViews; +}) { try { + const { orgPublicId, view } = argv; + debug('ℹ️ Options: ' + JSON.stringify({ orgPublicId, view })); // check SNYK_TOKEN is set as the sdk uses it getApiToken(); // TODO: define and pass options to help filter the response // based on filters available in API - const data = await generateLicenseData(argv.orgPublicId, {}); + const data = await generateLicenseData(orgPublicId, {}); console.log(JSON.stringify(data)); } catch (e) { console.error(e); diff --git a/src/lib/api/org/dependencies.ts b/src/lib/api/org/dependencies.ts index a54ab2d..ecf4da2 100644 --- a/src/lib/api/org/dependencies.ts +++ b/src/lib/api/org/dependencies.ts @@ -32,7 +32,7 @@ export async function getDependenciesDataForOrg( ); return dependenciesData; } catch (e) { - debug('Failed to fetch dependencies' + e); + debug('❌ Failed to fetch dependencies' + e); throw e; } } diff --git a/src/lib/api/org/licenses.ts b/src/lib/api/org/licenses.ts index 80a298c..9e747a7 100644 --- a/src/lib/api/org/licenses.ts +++ b/src/lib/api/org/licenses.ts @@ -27,7 +27,7 @@ export async function getLicenseDataForOrg( const licenseData = await getAllLicensesData(snykApiClient, body, sortBy, order); return licenseData; } catch (e) { - debug('Failed to fetch licenses' + e); + debug('❌ Failed to fetch licenses' + e); throw e; } } diff --git a/src/lib/generate-org-license-report.ts b/src/lib/generate-org-license-report.ts index c3a56a8..0bfe51a 100644 --- a/src/lib/generate-org-license-report.ts +++ b/src/lib/generate-org-license-report.ts @@ -17,20 +17,20 @@ export async function generateLicenseData( orgPublicId: string, options?, ): Promise { - debug(`Generating license data for Org:${orgPublicId}`); + debug(`ℹ️ Generating license data for Org:${orgPublicId}`); try { const licenseData = await getLicenseDataForOrg(orgPublicId, options); - debug(`Got license API data for Org:${orgPublicId}`); + debug(`✅ Got license API data for Org:${orgPublicId}`); const dependenciesDataRaw = await getDependenciesDataForOrg( orgPublicId, options, ); - debug(`Got dependencies API data for Org:${orgPublicId}`); + debug(`✅ Got dependencies API data for Org:${orgPublicId}`); const licenseReportData: LicenseReportData = {}; const dependenciesData = _.groupBy(dependenciesDataRaw.results, 'id'); // TODO: what if 0? - debug(`Processing ${licenseData.total} licenses`); + debug(`⏳ Processing ${licenseData.total} licenses`); const dependenciesAll = []; for (const license of licenseData.results) { @@ -55,9 +55,11 @@ export async function generateLicenseData( licenseUrl: licenseData?.licenseUrl, }; } + debug(`✅ Done processing ${licenseData.total} licenses`); + return licenseReportData; } catch (e) { - debug('Failed to generate report data', e); + debug('❌ Failed to generate report data', e); throw e; } } @@ -89,12 +91,12 @@ async function getLicenseTextAndUrl( try { return await fetchSpdxLicenseTextAndUrl(id); } catch (e) { - debug(`Failed to get license data for as SPDX, trying non-SPDX: ${id}`); + debug(`❌ Failed to get license data for as SPDX, trying non-SPDX: ${id}`); } try { return await fetchNonSpdxLicenseTextAndUrl(id); } catch (e) { - debug(`Failed to get license data as non-SPDX: ${id}`); + debug(`❌ Failed to get license data as non-SPDX: ${id}`); } return undefined; diff --git a/src/lib/generate-output/html/index.ts b/src/lib/generate-output/html/index.ts index 95cb8f2..05e32e8 100644 --- a/src/lib/generate-output/html/index.ts +++ b/src/lib/generate-output/html/index.ts @@ -1,8 +1,12 @@ import * as Handlebars from 'handlebars'; import * as path from 'path'; import * as fs from 'fs'; +import * as debugLib from 'debug'; + import { LicenseReportData } from '../../generate-org-license-report'; +const debug = debugLib('snyk-licenses:generateHtmlReport'); + export const enum SupportedViews { ORG_LICENSES = 'org-licenses', // TODO: support later diff --git a/test/system/json.test.ts b/test/system/json.test.ts index 61dff7d..6664d97 100644 --- a/test/system/json.test.ts +++ b/test/system/json.test.ts @@ -11,9 +11,9 @@ describe('`snyk-licenses-report json <...>`', () => { }); }); it('Generated JSON data with correct --orgPublicId', async (done) => { - exec(`node ${main} json --orgPublicId=${ORG_ID}`, (err, stdout) => { + exec(`DEBUG=snyk-license* node ${main} json --orgPublicId=${ORG_ID}`, (err, stdout) => { expect(err).toBeNull(); - console.log({err, stdout}) + console.log({err, stdout, ORG_ID}) expect(stdout.trim()).toMatch("BSD-2-Clause"); done(); }); From 9884f6a6c0519824da181364b30d6eca177c9a6b Mon Sep 17 00:00:00 2001 From: ghe Date: Mon, 28 Sep 2020 17:52:43 +0100 Subject: [PATCH 4/4] feat: basic html report & tests --- package.json | 2 +- src/cmds/generate.ts | 32 +++++++-- src/lib/generate-output/html/index.ts | 23 +++++-- .../html/templates/licenses-view.hbs | 33 ++++----- src/lib/write-contents-to-file.ts | 69 +++++++++++++++++++ .../generate-html-report.test.ts.snap | 25 +++++++ test/lib/generate-html-report.test.ts | 24 +++++++ test/lib/generate-license-report-data.test.ts | 1 + test/system/__snapshots__/json.test.ts.snap | 4 +- test/system/basic.test.ts | 5 ++ test/system/generate.test.ts | 34 ++++++--- test/system/json.test.ts | 19 ++--- 12 files changed, 221 insertions(+), 50 deletions(-) create mode 100644 src/lib/write-contents-to-file.ts create mode 100644 test/lib/__snapshots__/generate-html-report.test.ts.snap create mode 100644 test/lib/generate-html-report.test.ts diff --git a/package.json b/package.json index 32c45ea..88e2f73 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "debug": "4.1.1", "handlebars": "4.7.6", "lodash": "4.17.20", - "node-fetch": "2.6.0", + "node-fetch": "2.6.1", "snyk-api-ts-client": "1.5.0", "snyk-config": "^3.0.0", "source-map-support": "^0.5.16", diff --git a/src/cmds/generate.ts b/src/cmds/generate.ts index 56c3903..3f4c618 100644 --- a/src/cmds/generate.ts +++ b/src/cmds/generate.ts @@ -1,7 +1,16 @@ import * as debugLib from 'debug'; +import * as pathLib from 'path'; import { getApiToken } from '../lib/get-api-token'; -import { LicenseReportData, generateLicenseData } from '../lib/generate-org-license-report'; -import { generateHtmlReport, generatePdfReport, SupportedViews } from '../lib/generate-output'; +import { + LicenseReportData, + generateLicenseData, +} from '../lib/generate-org-license-report'; +import { + generateHtmlReport, + generatePdfReport, + SupportedViews, +} from '../lib/generate-output'; +import { writeContentsToFile } from '../lib/write-contents-to-file'; const debug = debugLib('snyk-licenses:generate'); const outputHandlers = { @@ -20,22 +29,24 @@ export const builder = { orgPublicId: { required: true, default: undefined, - desc: 'Public id of the organization in Snyk (available on organization settings)' + desc: + 'Public id of the organization in Snyk (available on organization settings)', }, template: { default: undefined, - desc: 'Path to custom Handelbars.js template file (*.hbs)' + desc: 'Path to custom Handelbars.js template file (*.hbs)', }, outputFormat: { default: OutputFormat.HTML, desc: 'Report format', // TODO: add also PDF when ready - options: [OutputFormat.HTML] + options: [OutputFormat.HTML], }, view: { // TODO: add also dependency based view when ready default: SupportedViews.ORG_LICENSES, - desc: 'How should the data be represented. Defaults to a license based view.', + desc: + 'How should the data be represented. Defaults to a license based view.', }, }; export const aliases = ['g']; @@ -57,7 +68,14 @@ export async function handler(argv: { orgPublicId, ); const generateReportFunc = outputHandlers[outputFormat]; - return await generateReportFunc(licenseData, template, view); + const res = await generateReportFunc(licenseData, template, view); + if (res) { + const outputFileName = `${orgPublicId}-${view}.html`; + const outputFile = pathLib.resolve(__dirname, outputFileName); + debug(`ℹ️ Saving generated report to ${outputFile}`); + writeContentsToFile(res, outputFile); + console.log('License report saved at ' + outputFile); + } } catch (e) { console.error(e); } diff --git a/src/lib/generate-output/html/index.ts b/src/lib/generate-output/html/index.ts index 05e32e8..838c673 100644 --- a/src/lib/generate-output/html/index.ts +++ b/src/lib/generate-output/html/index.ts @@ -6,6 +6,7 @@ import * as debugLib from 'debug'; import { LicenseReportData } from '../../generate-org-license-report'; const debug = debugLib('snyk-licenses:generateHtmlReport'); +const DEFAULT_TEMPLATE = './templates/licenses-view.hbs'; export const enum SupportedViews { ORG_LICENSES = 'org-licenses', @@ -15,19 +16,25 @@ export const enum SupportedViews { export async function generateHtmlReport( data: LicenseReportData, - templateOverridePath: string, - view: SupportedViews, + templateOverridePath: string | undefined = undefined, + view: SupportedViews = SupportedViews.ORG_LICENSES, ) { // TODO: add any helpers & data transformations that are useful here - + debug('ℹ️ Generating HTML report'); const hbsTemplate = selectTemplate(view, templateOverridePath); + debug( + `✅ Using template ${ + hbsTemplate === DEFAULT_TEMPLATE ? 'default template' : hbsTemplate + }`, + ); await registerPeerPartial(hbsTemplate, 'inline-css'); + debug(`✅ Registered Handlebars.js partials`); const htmlTemplate = await compileTemplate(hbsTemplate); - return htmlTemplate({hello: 1}); + debug(`✅ Compiled template ${hbsTemplate}`); + return htmlTemplate(data); } -function selectTemplate(view, templateOverride): string { - const DEFAULT_TEMPLATE = './templates/licenses-view.hbs'; +function selectTemplate(view: SupportedViews, templateOverride?): string { switch (view) { case SupportedViews.ORG_LICENSES: return templateOverride || DEFAULT_TEMPLATE; @@ -51,7 +58,9 @@ async function registerPeerPartial( async function compileTemplate( fileName: string, ): Promise { - return readFile(path.resolve(__dirname, fileName), 'utf8').then(Handlebars.compile); + return readFile(path.resolve(__dirname, fileName), 'utf8').then( + Handlebars.compile, + ); } function readFile(filePath: string, encoding: string): Promise { diff --git a/src/lib/generate-output/html/templates/licenses-view.hbs b/src/lib/generate-output/html/templates/licenses-view.hbs index 2e9c2e6..b17e16d 100644 --- a/src/lib/generate-output/html/templates/licenses-view.hbs +++ b/src/lib/generate-output/html/templates/licenses-view.hbs @@ -1,20 +1,21 @@ + + + + + + Snyk Licenses Report + + + {{!-- {{> inline-css }} --}} + - - - - - - Snyk Licenses Report - - - {{> inline-css }} - + +
+

Hello!

+
+ + - -
- {{data}} -
- diff --git a/src/lib/write-contents-to-file.ts b/src/lib/write-contents-to-file.ts new file mode 100644 index 0000000..838bfd5 --- /dev/null +++ b/src/lib/write-contents-to-file.ts @@ -0,0 +1,69 @@ +import { gte } from 'semver'; +import * as pathLib from 'path'; +import * as debugLib from 'debug'; +import { existsSync, mkdirSync, createWriteStream } from 'fs'; +export const MIN_VERSION_FOR_MKDIR_RECURSIVE = '10.12.0'; + +const debug = debugLib('snyk-licenses:writeContentsToFile'); + +function writeContentsToFileSwallowingErrors( + outputFile: string, + contents: string, +) { + try { + const ws = createWriteStream(outputFile, { flags: 'w' }); + ws.on('error', (err) => { + console.error(err); + }); + ws.write(contents); + ws.end('\n'); + } catch (err) { + console.error(err); + } +} + +export function writeContentsToFile(contents: string, outputFile: string) { + if (!outputFile) { + return; + } + + if (outputFile.constructor.name !== String.name) { + console.error('--json-output-file should be a filename path'); + return; + } + + // create the directory if it doesn't exist + const dirPath = pathLib.dirname(outputFile); + const createDirSuccess = createDirectory(dirPath); + if (createDirSuccess) { + writeContentsToFileSwallowingErrors(outputFile, contents); + } +} + +function createDirectory(newDirectoryFullPath: string): boolean { + // if the path already exists, true + // if we successfully create the directory, return true + // if we can't successfully create the directory, either because node < 10 and recursive or some other failure, catch the error and return false + + if (existsSync(newDirectoryFullPath)) { + return true; + } + + const nodeVersion = process.version; + + try { + if (gte(nodeVersion, MIN_VERSION_FOR_MKDIR_RECURSIVE)) { + // nodeVersion is >= 10.12.0 - required for mkdirsync recursive + const options: any = { recursive: true }; // TODO: remove this after we drop support for node v8 + mkdirSync(newDirectoryFullPath, options); + return true; + } else { + // nodeVersion is < 10.12.0 + mkdirSync(newDirectoryFullPath); + return true; + } + } catch (err) { + debug(`Could not create directory ${newDirectoryFullPath}: ${err}`); + return false; + } +} diff --git a/test/lib/__snapshots__/generate-html-report.test.ts.snap b/test/lib/__snapshots__/generate-html-report.test.ts.snap new file mode 100644 index 0000000..f845253 --- /dev/null +++ b/test/lib/__snapshots__/generate-html-report.test.ts.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Generate HTML report License HTML Report is generated as expected 1`] = ` +" + + + + + + + Snyk Licenses Report + + + + + +
+

Hello!

+
+ + + +" +`; diff --git a/test/lib/generate-html-report.test.ts b/test/lib/generate-html-report.test.ts new file mode 100644 index 0000000..6e5ef76 --- /dev/null +++ b/test/lib/generate-html-report.test.ts @@ -0,0 +1,24 @@ +import { + generateHtmlReport, +} from '../../src/lib/generate-output'; +import { generateLicenseData } from '../../src/lib/generate-org-license-report'; +describe('Generate HTML report', () => { + const OLD_ENV = process.env; + process.env.SNYK_TOKEN = process.env.SNYK_TEST_TOKEN; + const ORG_ID = process.env.TEST_ORG_ID as string; + + afterAll(async () => { + process.env = { ...OLD_ENV }; + }); + test('SNYK_TOKEN & ORG_ID are set', async () => { + expect(process.env.SNYK_TOKEN).not.toBeNull(); + expect(process.env.ORG_ID).not.toBeNull(); + }); + test('License HTML Report is generated as expected', async () => { + const licenseRes = await generateLicenseData(ORG_ID, {}); + const htmlData = await generateHtmlReport(licenseRes); + expect(htmlData).toMatchSnapshot(); + }, 50000); + + test.todo('Test for when API fails aka bad org id provided'); +}); diff --git a/test/lib/generate-license-report-data.test.ts b/test/lib/generate-license-report-data.test.ts index 6322b0f..5c7a0a3 100644 --- a/test/lib/generate-license-report-data.test.ts +++ b/test/lib/generate-license-report-data.test.ts @@ -1,4 +1,5 @@ import { generateLicenseData } from '../../src/lib/generate-org-license-report'; +import { generateHtmlReport } from '../../src/lib/generate-output'; describe('Get org licenses', () => { const OLD_ENV = process.env; diff --git a/test/system/__snapshots__/json.test.ts.snap b/test/system/__snapshots__/json.test.ts.snap index 6270fd1..19ebe42 100644 --- a/test/system/__snapshots__/json.test.ts.snap +++ b/test/system/__snapshots__/json.test.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`\`snyk-licenses-report json <...>\` Shows error when missing --orgPublicId 1`] = ` -"Command failed: node ./dist/index.js json +"Command failed: DEBUG=* node ./dist/index.js json index.js json Generate org licenses & dependencies data in JSON format @@ -10,6 +10,8 @@ Options: --version Show version number [boolean] --help Show help [boolean] --orgPublicId [required] + --view How should the data be represented. Defaults to a license based + view. [default: \\"org-licenses\\"] Missing required argument: orgPublicId" `; diff --git a/test/system/basic.test.ts b/test/system/basic.test.ts index 06ec6a7..19d9c9b 100644 --- a/test/system/basic.test.ts +++ b/test/system/basic.test.ts @@ -3,6 +3,11 @@ import { sep } from 'path'; const main = './dist/index.js'.replace(/\//g, sep); describe('`snyk-licenses-report help <...>`', () => { + const OLD_ENV = process.env; + process.env.SNYK_TOKEN = process.env.SNYK_TEST_TOKEN; + afterAll(async () => { + process.env = { ...OLD_ENV }; + }); it('Shows help text as expected', async (done) => { return exec(`node ${main} help`, (err, stdout) => { if (err) { diff --git a/test/system/generate.test.ts b/test/system/generate.test.ts index ea7d3d3..cb67486 100644 --- a/test/system/generate.test.ts +++ b/test/system/generate.test.ts @@ -6,17 +6,31 @@ const ORG_ID = process.env.TEST_ORG_ID as string; describe('`snyk-licenses-report generate <...>`', () => { it('Shows error when missing --orgPublicId', async (done) => { - exec(`node ${main} generate`, (err, stdout) => { - expect(stdout).toBe(""); - expect(err.message.trim()).toMatchSnapshot(); - done(); - }); + exec( + `node ${main} generate`, + { env: { SNYK_TOKEN: process.env.SNYK_TEST_TOKEN } }, + (err, stdout) => { + expect(stdout).toBe(''); + expect(err.message.trim()).toMatchSnapshot(); + done(); + }, + ); }); - it.todo("generated the report successfully with default params"); - it.todo("generated the report successfully with custom template") - it.todo("generated the report successfully with custom template") + it('generated the report successfully with default params', (done) => { + exec( + `node ${main} generate --orgPublicId=${ORG_ID}`, + { env: { SNYK_TOKEN: process.env.SNYK_TEST_TOKEN } }, + (err, stdout) => { + expect(stdout).toMatch('License report saved at'); + expect(err).toBeNull(); + done(); + }, + ); + }, 50000); + it.todo('generated the report successfully with custom template'); + it.todo('generated the report successfully with custom template'); - it.todo("API is down"); - it.todo("Requested org has no licenses policy"); + it.todo('API is down'); + it.todo('Requested org has no licenses policy'); }); diff --git a/test/system/json.test.ts b/test/system/json.test.ts index 6664d97..d9032b3 100644 --- a/test/system/json.test.ts +++ b/test/system/json.test.ts @@ -4,18 +4,21 @@ const main = './dist/index.js'.replace(/\//g, sep); const ORG_ID = process.env.TEST_ORG_ID as string; describe('`snyk-licenses-report json <...>`', () => { it('Shows error when missing --orgPublicId', async (done) => { - exec(`node ${main} json`, (err, stdout) => { - expect(stdout).toBe(""); + exec(`DEBUG=* node ${main} json`, (err, stdout) => { + expect(stdout).toBe(''); expect(err.message.trim()).toMatchSnapshot(); done(); }); }); it('Generated JSON data with correct --orgPublicId', async (done) => { - exec(`DEBUG=snyk-license* node ${main} json --orgPublicId=${ORG_ID}`, (err, stdout) => { - expect(err).toBeNull(); - console.log({err, stdout, ORG_ID}) - expect(stdout.trim()).toMatch("BSD-2-Clause"); - done(); - }); + exec( + `node ${main} json --orgPublicId=${ORG_ID}`, + { env: { SNYK_TOKEN: process.env.SNYK_TEST_TOKEN } }, + (err, stdout, stderr) => { + expect(err).toBeNull(); + expect(stdout.trim()).toMatch('BSD-2-Clause'); + done(); + }, + ); }, 30000); });