From 7c7c68bb624779da73d5bdde25003027f19abeac Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Wed, 7 Aug 2024 15:05:24 -0600 Subject: [PATCH] test: more tests --- package.json | 2 +- src/components/multi-stage-output.tsx | 4 +- test/components/multi-stage-output.test.ts | 1 - test/components/multi-stage-output.test.tsx | 201 ++++++++++++++++++++ 4 files changed, 204 insertions(+), 4 deletions(-) delete mode 100644 test/components/multi-stage-output.test.ts create mode 100644 test/components/multi-stage-output.test.tsx diff --git a/package.json b/package.json index 9fbfdfa..915f976 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@oclif/multi-stage-output", - "description": "Terminal output for commands with multiple stages", + "description": "Terminal output for oclif commands with multiple stages", "version": "0.0.0", "author": "Salesforce", "bugs": "https://github.com/oclif/multi-stage-output/issues", diff --git a/src/components/multi-stage-output.tsx b/src/components/multi-stage-output.tsx index 3e240f7..a85a13a 100644 --- a/src/components/multi-stage-output.tsx +++ b/src/components/multi-stage-output.tsx @@ -59,7 +59,7 @@ type StageInfoBlock> = Array< (KeyValuePair & {stage: string}) | (SimpleMessage & {stage: string}) > -type FormattedKeyValue = { +export type FormattedKeyValue = { readonly color?: string readonly isBold?: boolean // eslint-disable-next-line react/no-unused-prop-types @@ -198,7 +198,7 @@ function Infos({ ) } -function Stages({ +export function Stages({ error, hasElapsedTime = true, hasStageTime = true, diff --git a/test/components/multi-stage-output.test.ts b/test/components/multi-stage-output.test.ts deleted file mode 100644 index 5fd7ef3..0000000 --- a/test/components/multi-stage-output.test.ts +++ /dev/null @@ -1 +0,0 @@ -// eslint-disable-next-line unicorn/no-empty-file diff --git a/test/components/multi-stage-output.test.tsx b/test/components/multi-stage-output.test.tsx new file mode 100644 index 0000000..3b5dacd --- /dev/null +++ b/test/components/multi-stage-output.test.tsx @@ -0,0 +1,201 @@ +import {config, expect} from 'chai' +import {render} from 'ink-testing-library' +import React from 'react' +import stripAnsi from 'strip-ansi' + +import {FormattedKeyValue, Stages} from '../../src/components/multi-stage-output.js' +import {StageTracker} from '../../src/stage-tracker.js' + +config.truncateThreshold = 0 + +function lastValidFrame(frames: string[]): string { + for (let i = frames.length - 1; i >= 0; i--) { + if (frames[i] !== '\n') return stripAnsi(frames[i]) + } + + return '' +} + +describe('Stages', () => { + it('should render pending stages', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + const {frames, unmount} = render() + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include('─ Test ─') + expect(lastFrame).to.include('◼ Step1') + expect(lastFrame).to.include('◼ Step2') + expect(lastFrame).to.include('Elapsed Time:') + }) + + it('should render completed stages', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + stageTracker.set('step1', 'completed') + const {frames, unmount} = render() + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include('─ Test ─') + expect(lastFrame).to.include('✔ Step1') + expect(lastFrame).to.include('◼ Step2') + expect(lastFrame).to.include('Elapsed Time:') + }) + + it('should render skipped stages', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + stageTracker.set('step1', 'skipped') + stageTracker.set('step2', 'completed') + const {frames, unmount} = render() + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include('─ Test ─') + expect(lastFrame).to.include('◯ Step1 - Skipped') + expect(lastFrame).to.include('✔ Step2') + expect(lastFrame).to.include('Elapsed Time:') + }) + + it('should render failed stages', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + stageTracker.set('step1', 'failed') + const {frames, unmount} = render() + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include('─ Test ─') + expect(lastFrame).to.include('✘ Step1') + expect(lastFrame).to.include('◼ Step2') + expect(lastFrame).to.include('Elapsed Time:') + }) + + it('should disable elapsed time', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + const {frames, unmount} = render() + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include('─ Test ─') + expect(lastFrame).to.include('◼ Step1') + expect(lastFrame).to.include('◼ Step2') + expect(lastFrame).to.not.include('Elapsed Time:') + }) + + it('should enable stage time', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + stageTracker.set('step1', 'completed') + const {frames, unmount} = render() + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include('─ Test ─') + expect(lastFrame).to.include('✔ Step1 0ms') + expect(lastFrame).to.include('◼ Step2\n') + expect(lastFrame).to.include('Elapsed Time:') + }) + + it('should disable stage time', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + stageTracker.set('step1', 'completed') + const {frames, unmount} = render() + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include('─ Test ─') + expect(lastFrame).to.include('✔ Step1\n') + expect(lastFrame).to.include('◼ Step2\n') + expect(lastFrame).to.include('Elapsed Time:') + }) + + it('should show pre-stage info block', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + + const preStagesBlock: FormattedKeyValue[] = [ + { + type: 'message', + value: 'this is a message', + }, + { + label: 'Static', + type: 'static-key-value', + value: 'this is a static key:value pair', + }, + { + label: 'Dynamic', + type: 'dynamic-key-value', + value: 'this is a dynamic key:value pair', + }, + ] + + const {frames, unmount} = render( + , + ) + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include(` this is a message + Static: this is a static key:value pair + Dynamic: this is a dynamic key:value pair + + ◼ Step1 +`) + }) + + it('should show post-stage info block', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + + const postStagesBlock: FormattedKeyValue[] = [ + { + type: 'message', + value: 'this is a message', + }, + { + label: 'Static', + type: 'static-key-value', + value: 'this is a static key:value pair', + }, + { + label: 'Dynamic', + type: 'dynamic-key-value', + value: 'this is a dynamic key:value pair', + }, + ] + + const {frames, unmount} = render( + , + ) + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include(` ◼ Step2 + + this is a message + Static: this is a static key:value pair + Dynamic: this is a dynamic key:value pair`) + }) + + it('should show stage specific info block', async () => { + const stageTracker = new StageTracker(['step1', 'step2']) + stageTracker.set('step1', 'completed') + const stageSpecificBlock: FormattedKeyValue[] = [ + { + stage: 'step1', + type: 'message', + value: 'this is a message', + }, + { + label: 'Static', + stage: 'step1', + type: 'static-key-value', + value: 'this is a static key:value pair', + }, + { + label: 'Dynamic', + stage: 'step1', + type: 'dynamic-key-value', + value: 'this is a dynamic key:value pair', + }, + ] + + const {frames, unmount} = render( + , + ) + unmount() + const lastFrame = lastValidFrame(frames) + expect(lastFrame).to.include(` ✔ Step1 0ms + this is a message + Static: this is a static key:value pair + Dynamic: this is a dynamic key:value pair`) + }) +})