diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 2af19f1..bbaf610 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,93 +1,108 @@ -# 1.1.1 +# Change Log + +All notable changes to the "@qavajs/html-formatter" will be documented in this file. + +Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. + +:rocket: - new feature +:beetle: - bugfix +:x: - deprecation/removal +:pencil: - chore +:microscope: - experimental + +# [1.2.0] +- :rocket: added slideshow for screenshot attachments + +# [1.1.1] - fixed issue with required `showLogs` -# 1.1.0 +# [1.1.0] - added `showLogs` flag to enable/disable `text/x.cucumber.log+plain` logs -# 0.18.1 +# [0.18.1] - fixed home navigation -# 0.18.0 +# [0.18.0] - added capability to display http request and response - updated main menu implementation -# 0.17.0 +# [0.17.0] - implemented runtime streaming -# 0.16.0 +# [0.16.0] - added dark theme -# 0.15.4 +# [0.15.4] - fixed multiple tags displaying -# 0.15.3 +# [0.15.3] - fixed logs overlay width -# 0.15.2 +# [0.15.2] - fixed duplicated scenarios in case of retries -# 0.15.1 +# [0.15.1] - fixed nano seconds conversion -# 0.15.0 +# [0.15.0] - migrated to react 18 and vite dev server - added displaying of execution time for steps and scenarios - -# 0.14.4 + +# [0.14.4] - fixed [Feature that contains only undefined/skipped scenarios is displayed as passed](https://github.com/qavajs/html-formatter/issues/36) -# 0.14.3 +# [0.14.3] - fixed width and height of error and logs overlay -# 0.14.1 +# [0.14.1] - fixed stacktrace overlay - fixed multiline and data table displaying -# 0.14.0 +# [0.14.0] - added support of named attachments -# 0.13.0 +# [0.13.0] - replaced legacy json formatter with custom one based on cucumber messages - added support of named hooks -# 0.0.11 +# [0.0.11] - fixed $' replace issue -# 0.0.10 +# [0.0.10] - fixed issue of not escaped chars in logs -# 0.0.9 +# [0.0.9] - fixed issue of not opening features with /%# in names -# 0.0.8 +# [0.0.8] - added capability to attach cucumber logs -# 0.0.7 +# [0.0.7] - fixed duplicated results in case of same feature name -# 0.0.6 +# [0.0.6] - added unsupported attachment handler - added multiple attachment support - added mime type display for attachments - added feature names to failed page - added possibility to save state of "show only failed" switch -# 0.0.5 +# [0.0.5] - added capability to write tag name to clipboard - added html attachments - added capability to open attachment in new tab -- added capability to pass metadata +- added capability to pass metadata -# 0.0.4 +# [0.0.4] - added support application/json -# 0.0.3 +# [0.0.3] - fixed image fit - fixed tags fit in title -# 0.0.2 +# [0.0.2] - added feature titles - minor fixes -# 0.0.1 +# [0.0.1] - initial implementation diff --git a/formatter/index.html b/formatter/index.html new file mode 100644 index 0000000..ee67ee3 --- /dev/null +++ b/formatter/index.html @@ -0,0 +1,61 @@ + + + + + + + qavajs html report + + + + +
+ + + diff --git a/package-lock.json b/package-lock.json index 06f24a9..6965195 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@qavajs/html-formatter", - "version": "1.1.1", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@qavajs/html-formatter", - "version": "1.1.1", + "version": "1.2.0", "license": "MIT", "devDependencies": { "@cucumber/cucumber": "^12.7.0", @@ -3142,9 +3142,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4878,9 +4878,9 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -5162,9 +5162,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -6933,9 +6933,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, "license": "ISC", "bin": { diff --git a/package.json b/package.json index 7bd1c4a..abf8fdb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@qavajs/html-formatter", - "version": "1.1.1", + "version": "1.2.0", "main": "formatter/formatter.js", "repository": { "type": "git", diff --git a/src/components/AttachmentModal.tsx b/src/components/AttachmentModal.tsx index f58af60..a7ade87 100644 --- a/src/components/AttachmentModal.tsx +++ b/src/components/AttachmentModal.tsx @@ -32,7 +32,7 @@ const Attachment = (props: any) => { } if (json.includes(props.embedding.mime_type)) { const pretty = JSON.stringify(JSON.parse(props.embedding.data), null, 2) - return
{pretty}
+ return
{pretty}
} if (iframe.includes(props.embedding.mime_type)) { return
@@ -53,7 +53,7 @@ export const AttachmentModal = (modalProps: any) => { modalProps.success('close')}/> - + diff --git a/src/components/SlideshowModal.tsx b/src/components/SlideshowModal.tsx new file mode 100644 index 0000000..bdef69e --- /dev/null +++ b/src/components/SlideshowModal.tsx @@ -0,0 +1,87 @@ +import { useState, useEffect, useCallback } from 'react'; +import { + ModalBlocker, + ModalFooter, + ModalHeader, + ModalWindow, + FlexRow, + Panel, + ScrollBars, + LinkButton, + IconButton, + Text +} from '@epam/loveship'; +import { openInNewTab } from '../utils/openInNewTab'; + +import PrevIcon from '@epam/assets/icons/common/navigation-chevron-left_left-24.svg?react'; +import NextIcon from '@epam/assets/icons/common/navigation-chevron-right_right-24.svg?react'; + +export const SlideshowModal = (modalProps: any) => { + const { embeddings } = modalProps; + const [currentIndex, setCurrentIndex] = useState(0); + const current = embeddings[currentIndex]; + const hasMultiple = embeddings.length > 1; + + const goPrev = useCallback(() => { + setCurrentIndex(i => (i > 0 ? i - 1 : embeddings.length - 1)); + }, [embeddings.length]); + + const goNext = useCallback(() => { + setCurrentIndex(i => (i < embeddings.length - 1 ? i + 1 : 0)); + }, [embeddings.length]); + + useEffect(() => { + const handleKey = (e: KeyboardEvent) => { + if (e.key === 'ArrowLeft') goPrev(); + if (e.key === 'ArrowRight') goNext(); + }; + window.addEventListener('keydown', handleKey); + return () => window.removeEventListener('keydown', handleKey); + }, [goPrev, goNext]); + + return ( + + + + modalProps.success('close')} /> + + + {hasMultiple && ( +
+ +
+ )} +
+ + + + {current.fileName +
+ {hasMultiple && ( +
+ +
+ )} +
+ {hasMultiple && ( + + + {current.fileName ? `${current.fileName} — ` : ''}{currentIndex + 1} / {embeddings.length} + + + )} +
+ +
+
+
+ ); +}; \ No newline at end of file diff --git a/src/components/Step.tsx b/src/components/Step.tsx index 8698721..67410a3 100644 --- a/src/components/Step.tsx +++ b/src/components/Step.tsx @@ -19,8 +19,9 @@ import ResponseIcon from '@epam/assets/icons/common/content-code_braces-24.svg?r import css from '../App.module.scss'; import { useUuiContext } from '@epam/uui-core'; -import { supportedMimeTypes } from '../utils/supportedMimeTypes'; +import { supportedMimeTypes, image } from '../utils/supportedMimeTypes'; import { openInNewTab } from '../utils/openInNewTab'; +import { SlideshowModal } from './SlideshowModal'; const logPlainMimeType = 'text/x.cucumber.log+plain'; const responseMimeType = 'text/x.response.json'; @@ -71,6 +72,10 @@ const handleAttachmentClick = (embedding: any, svc: any) => { return openInNewTab(embedding.data, embedding.mime_type) } +const handleSlideshowClick = (embeddings: any[], svc: any) => { + return () => svc.uuiModals.show((props: any) => ) +} + const handleLogsClick = (logs: any[], svc: any) => { return () => svc.uuiModals.show((props: any) => ) } @@ -83,7 +88,9 @@ export const Step = ({step}: {step: any}) => { const svc = useUuiContext(); const logs = step.embeddings?.filter((embedding: any) => embedding.mime_type === logPlainMimeType) ?? []; const responses = step.embeddings?.filter((embedding: any) => embedding.mime_type === responseMimeType) ?? []; - const attachments = step.embeddings?.filter((embedding: any) => ![logPlainMimeType, responseMimeType].includes(embedding.mime_type)) ?? []; + const allAttachments = step.embeddings?.filter((embedding: any) => ![logPlainMimeType, responseMimeType].includes(embedding.mime_type)) ?? []; + const screenshots = allAttachments.filter((embedding: any) => image.includes(embedding.mime_type)); + const attachments = allAttachments.filter((embedding: any) => !image.includes(embedding.mime_type)); return
{icon(step.result.status)} @@ -106,6 +113,11 @@ export const Step = ({step}: {step: any}) => { onClick={ handleResponseClick(embedding, svc) } />) } + {screenshots.length > 0 && 1 ? `Screenshots (${screenshots.length})` : (screenshots[0].fileName ?? 'Screenshot') } + onClick={ handleSlideshowClick(screenshots, svc) } + />} {attachments .map((embedding: any, index: any) =>