diff --git a/src/components/app/TestRunHooks.module.scss b/src/components/app/TestRunHooks.module.scss new file mode 100644 index 00000000..32fdd134 --- /dev/null +++ b/src/components/app/TestRunHooks.module.scss @@ -0,0 +1,12 @@ +.hooks { + padding: 0; + list-style: none; + + > li { + list-style: none; + + &:not(:empty) { + padding: 0.125em 0; + } + } +} diff --git a/src/components/app/TestRunHooks.stories.tsx b/src/components/app/TestRunHooks.stories.tsx new file mode 100644 index 00000000..eaa04aa1 --- /dev/null +++ b/src/components/app/TestRunHooks.stories.tsx @@ -0,0 +1,41 @@ +import * as messages from '@cucumber/messages' +import { Story } from '@ladle/react' +import React from 'react' + +import globalHooksAfterAllError from '../../../acceptance/global-hooks-afterall-error/global-hooks-afterall-error.js' +import globalHooksAttachments from '../../../acceptance/global-hooks-attachments/global-hooks-attachments.js' +import globalHooksBeforeAllError from '../../../acceptance/global-hooks-beforeall-error/global-hooks-beforeall-error.js' +import { EnvelopesProvider, FilteredDocuments } from './index.js' +import { TestRunHooks } from './TestRunHooks.js' + +type TemplateArgs = { + envelopes: readonly messages.Envelope[] +} + +const Template: Story = ({ envelopes }) => { + return ( + + + + + ) +} + +export default { + title: 'App/TestRunHooksList', +} + +export const GlobalHooksWithAttachments = Template.bind({}) +GlobalHooksWithAttachments.args = { + envelopes: globalHooksAttachments, +} + +export const GlobalHooksBeforeAllError = Template.bind({}) +GlobalHooksBeforeAllError.args = { + envelopes: globalHooksBeforeAllError, +} + +export const GlobalHooksAfterAllError = Template.bind({}) +GlobalHooksAfterAllError.args = { + envelopes: globalHooksAfterAllError, +} diff --git a/src/components/app/TestRunHooks.tsx b/src/components/app/TestRunHooks.tsx new file mode 100644 index 00000000..e8db6c98 --- /dev/null +++ b/src/components/app/TestRunHooks.tsx @@ -0,0 +1,24 @@ +import React, { FC } from 'react' + +import { useTestRunHooks } from '../../hooks/useTestRunHooks.js' +import { TestRunHookOutcome } from '../results/TestRunHookOutcome.js' +import styles from './TestRunHooks.module.scss' + +export const TestRunHooks: FC = () => { + const hooks = useTestRunHooks() + + return ( +
    + {hooks.map(({ testRunHookFinished, hook }) => { + return ( + + ) + })} +
+ ) +} diff --git a/src/components/app/index.ts b/src/components/app/index.ts index bae27ae0..44deede6 100644 --- a/src/components/app/index.ts +++ b/src/components/app/index.ts @@ -10,4 +10,5 @@ export * from './NoMatchResult.js' export * from './QueriesProvider.js' export * from './SearchBar.js' export * from './StatusesSummary.js' +export * from './TestRunHooks.js' export * from './UrlSearchProvider.js' diff --git a/src/components/results/TestRunHookOutcome.module.scss b/src/components/results/TestRunHookOutcome.module.scss new file mode 100644 index 00000000..be318e50 --- /dev/null +++ b/src/components/results/TestRunHookOutcome.module.scss @@ -0,0 +1,36 @@ +.header { + display: flex; + width: 100%; + gap: 0.25rem; +} + +.status { + padding-top: 0.1em; +} + +.title { + > * { + vertical-align: middle; + + & + * { + margin-left: 0.5em; + } + } +} + +.name { + font-size: 1em; + display: inline; + font-weight: normal; + padding: 0; + margin: 0; +} + +.content { + margin-left: 1.125rem; + + // bits of detail (doc string, data table, error, attachments) get consistent spacing between them + > * { + margin-top: 0.25rem !important; + } +} diff --git a/src/components/results/TestRunHookOutcome.tsx b/src/components/results/TestRunHookOutcome.tsx new file mode 100644 index 00000000..1c665e62 --- /dev/null +++ b/src/components/results/TestRunHookOutcome.tsx @@ -0,0 +1,35 @@ +import { Hook, HookType, TestRunHookFinished } from '@cucumber/messages' +import React, { FC } from 'react' + +import { StatusIcon } from '../gherkin/index.js' +import styles from './TestRunHookOutcome.module.scss' +import { TestStepAttachments } from './TestStepAttachments.js' +import { TestStepDuration } from './TestStepDuration.js' +import { TestStepResultDetails } from './TestStepResultDetails.js' + +interface Props { + hook: Hook + testRunHookFinished: TestRunHookFinished +} + +export const TestRunHookOutcome: FC = ({ hook, testRunHookFinished }) => { + return ( +
  • +
    +
    + +
    +
    +

    + {hook.name ?? (hook.type === HookType.BEFORE_TEST_RUN ? 'BeforeAll' : 'AfterAll')} +

    + +
    +
    +
    + + +
    +
  • + ) +} diff --git a/src/components/results/TestStepAttachments.tsx b/src/components/results/TestStepAttachments.tsx index 6da6a371..6003718f 100644 --- a/src/components/results/TestStepAttachments.tsx +++ b/src/components/results/TestStepAttachments.tsx @@ -1,4 +1,4 @@ -import { TestStepFinished } from '@cucumber/messages' +import { TestRunHookFinished, TestStepFinished } from '@cucumber/messages' import React from 'react' import { FC } from 'react' @@ -7,12 +7,12 @@ import { Attachment } from '../gherkin/index.js' import styles from './TestStepAttachments.module.scss' interface Props { - testStepFinished: TestStepFinished + testStepOrHookFinished: TestStepFinished | TestRunHookFinished } -export const TestStepAttachments: FC = ({ testStepFinished }) => { +export const TestStepAttachments: FC = ({ testStepOrHookFinished }) => { const { cucumberQuery } = useQueries() - const attachments = cucumberQuery.findAttachmentsBy(testStepFinished) + const attachments = cucumberQuery.findAttachmentsBy(testStepOrHookFinished) return (
      {attachments.map((attachment, index) => { diff --git a/src/components/results/TestStepOutcome.tsx b/src/components/results/TestStepOutcome.tsx index dfbf487e..b8b2c60e 100644 --- a/src/components/results/TestStepOutcome.tsx +++ b/src/components/results/TestStepOutcome.tsx @@ -33,7 +33,7 @@ export const TestStepOutcome: FC = ({ testStep, testStepFinished }) => {
      {testStep.pickleStepId && } - +
      ) diff --git a/src/hooks/helpers.ts b/src/hooks/helpers.ts new file mode 100644 index 00000000..721b9149 --- /dev/null +++ b/src/hooks/helpers.ts @@ -0,0 +1,6 @@ +export function ensure(value: T | undefined, message: string): T { + if (!value) { + throw new Error(message) + } + return value +} diff --git a/src/hooks/useTestRunHooks.ts b/src/hooks/useTestRunHooks.ts new file mode 100644 index 00000000..4b8a298b --- /dev/null +++ b/src/hooks/useTestRunHooks.ts @@ -0,0 +1,24 @@ +import { Hook, TestRunHookFinished } from '@cucumber/messages' + +import { ensure } from './helpers.js' +import { useQueries } from './useQueries.js' + +type RunHooksList = { testRunHookFinished: TestRunHookFinished; hook: Hook }[] + +export function useTestRunHooks(): RunHooksList { + const { cucumberQuery } = useQueries() + const testRunHooksFinished: ReadonlyArray = + cucumberQuery.findAllTestRunHookFinished() + + return testRunHooksFinished.map((testRunHookFinished) => { + const hook = ensure( + cucumberQuery.findHookBy(testRunHookFinished), + 'Expected testRunHookFinished to resolve with a hook' + ) + + return { + testRunHookFinished, + hook, + } + }) +}