@shirokuma-library/shirokuma-docs 0.1.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @shirokuma-library/shirokuma-docs might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/README.en.md +308 -0
- package/README.md +308 -0
- package/THIRD_PARTY_NOTICES.md +18 -0
- package/bin/shirokuma-docs +2 -0
- package/dist/analyzers/details-test-analysis.d.ts +31 -0
- package/dist/analyzers/details-test-analysis.d.ts.map +1 -0
- package/dist/analyzers/details-test-analysis.js +172 -0
- package/dist/analyzers/details-test-analysis.js.map +1 -0
- package/dist/analyzers/feature-map-builder.d.ts +20 -0
- package/dist/analyzers/feature-map-builder.d.ts.map +1 -0
- package/dist/analyzers/feature-map-builder.js +154 -0
- package/dist/analyzers/feature-map-builder.js.map +1 -0
- package/dist/analyzers/feature-map-references.d.ts +34 -0
- package/dist/analyzers/feature-map-references.d.ts.map +1 -0
- package/dist/analyzers/feature-map-references.js +249 -0
- package/dist/analyzers/feature-map-references.js.map +1 -0
- package/dist/analyzers/reference-analyzer.d.ts +95 -0
- package/dist/analyzers/reference-analyzer.d.ts.map +1 -0
- package/dist/analyzers/reference-analyzer.js +372 -0
- package/dist/analyzers/reference-analyzer.js.map +1 -0
- package/dist/commands/adr.d.ts +26 -0
- package/dist/commands/adr.d.ts.map +1 -0
- package/dist/commands/adr.js +129 -0
- package/dist/commands/adr.js.map +1 -0
- package/dist/commands/api-tools.d.ts +83 -0
- package/dist/commands/api-tools.d.ts.map +1 -0
- package/dist/commands/api-tools.js +775 -0
- package/dist/commands/api-tools.js.map +1 -0
- package/dist/commands/coverage.d.ts +139 -0
- package/dist/commands/coverage.d.ts.map +1 -0
- package/dist/commands/coverage.js +481 -0
- package/dist/commands/coverage.js.map +1 -0
- package/dist/commands/deps.d.ts +24 -0
- package/dist/commands/deps.d.ts.map +1 -0
- package/dist/commands/deps.js +211 -0
- package/dist/commands/deps.js.map +1 -0
- package/dist/commands/details-context.d.ts +38 -0
- package/dist/commands/details-context.d.ts.map +1 -0
- package/dist/commands/details-context.js +193 -0
- package/dist/commands/details-context.js.map +1 -0
- package/dist/commands/details-types.d.ts +315 -0
- package/dist/commands/details-types.d.ts.map +1 -0
- package/dist/commands/details-types.js +7 -0
- package/dist/commands/details-types.js.map +1 -0
- package/dist/commands/details.d.ts +24 -0
- package/dist/commands/details.d.ts.map +1 -0
- package/dist/commands/details.js +299 -0
- package/dist/commands/details.js.map +1 -0
- package/dist/commands/discussion-templates.d.ts +26 -0
- package/dist/commands/discussion-templates.d.ts.map +1 -0
- package/dist/commands/discussion-templates.js +270 -0
- package/dist/commands/discussion-templates.js.map +1 -0
- package/dist/commands/discussions.d.ts +31 -0
- package/dist/commands/discussions.d.ts.map +1 -0
- package/dist/commands/discussions.js +743 -0
- package/dist/commands/discussions.js.map +1 -0
- package/dist/commands/feature-map-types.d.ts +294 -0
- package/dist/commands/feature-map-types.d.ts.map +1 -0
- package/dist/commands/feature-map-types.js +8 -0
- package/dist/commands/feature-map-types.js.map +1 -0
- package/dist/commands/feature-map.d.ts +30 -0
- package/dist/commands/feature-map.d.ts.map +1 -0
- package/dist/commands/feature-map.js +137 -0
- package/dist/commands/feature-map.js.map +1 -0
- package/dist/commands/generate.d.ts +16 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +88 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/gh-discussions.d.ts +31 -0
- package/dist/commands/gh-discussions.d.ts.map +1 -0
- package/dist/commands/gh-discussions.js +743 -0
- package/dist/commands/gh-discussions.js.map +1 -0
- package/dist/commands/gh-issues-pr.d.ts +74 -0
- package/dist/commands/gh-issues-pr.d.ts.map +1 -0
- package/dist/commands/gh-issues-pr.js +417 -0
- package/dist/commands/gh-issues-pr.js.map +1 -0
- package/dist/commands/gh-issues.d.ts +90 -0
- package/dist/commands/gh-issues.d.ts.map +1 -0
- package/dist/commands/gh-issues.js +1297 -0
- package/dist/commands/gh-issues.js.map +1 -0
- package/dist/commands/gh-projects.d.ts +54 -0
- package/dist/commands/gh-projects.d.ts.map +1 -0
- package/dist/commands/gh-projects.js +966 -0
- package/dist/commands/gh-projects.js.map +1 -0
- package/dist/commands/gh-repo.d.ts +18 -0
- package/dist/commands/gh-repo.d.ts.map +1 -0
- package/dist/commands/gh-repo.js +253 -0
- package/dist/commands/gh-repo.js.map +1 -0
- package/dist/commands/github-data.d.ts +67 -0
- package/dist/commands/github-data.d.ts.map +1 -0
- package/dist/commands/github-data.js +361 -0
- package/dist/commands/github-data.js.map +1 -0
- package/dist/commands/i18n.d.ts +102 -0
- package/dist/commands/i18n.d.ts.map +1 -0
- package/dist/commands/i18n.js +829 -0
- package/dist/commands/i18n.js.map +1 -0
- package/dist/commands/impact.d.ts +14 -0
- package/dist/commands/impact.d.ts.map +1 -0
- package/dist/commands/impact.js +263 -0
- package/dist/commands/impact.js.map +1 -0
- package/dist/commands/init.d.ts +53 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +429 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/issues-pr.d.ts +74 -0
- package/dist/commands/issues-pr.d.ts.map +1 -0
- package/dist/commands/issues-pr.js +417 -0
- package/dist/commands/issues-pr.js.map +1 -0
- package/dist/commands/issues.d.ts +76 -0
- package/dist/commands/issues.d.ts.map +1 -0
- package/dist/commands/issues.js +1285 -0
- package/dist/commands/issues.js.map +1 -0
- package/dist/commands/link-docs.d.ts +21 -0
- package/dist/commands/link-docs.d.ts.map +1 -0
- package/dist/commands/link-docs.js +990 -0
- package/dist/commands/link-docs.js.map +1 -0
- package/dist/commands/lint-annotations.d.ts +28 -0
- package/dist/commands/lint-annotations.d.ts.map +1 -0
- package/dist/commands/lint-annotations.js +511 -0
- package/dist/commands/lint-annotations.js.map +1 -0
- package/dist/commands/lint-code.d.ts +26 -0
- package/dist/commands/lint-code.d.ts.map +1 -0
- package/dist/commands/lint-code.js +428 -0
- package/dist/commands/lint-code.js.map +1 -0
- package/dist/commands/lint-coverage.d.ts +33 -0
- package/dist/commands/lint-coverage.d.ts.map +1 -0
- package/dist/commands/lint-coverage.js +379 -0
- package/dist/commands/lint-coverage.js.map +1 -0
- package/dist/commands/lint-docs.d.ts +23 -0
- package/dist/commands/lint-docs.d.ts.map +1 -0
- package/dist/commands/lint-docs.js +338 -0
- package/dist/commands/lint-docs.js.map +1 -0
- package/dist/commands/lint-structure.d.ts +38 -0
- package/dist/commands/lint-structure.d.ts.map +1 -0
- package/dist/commands/lint-structure.js +350 -0
- package/dist/commands/lint-structure.js.map +1 -0
- package/dist/commands/lint-tests.d.ts +25 -0
- package/dist/commands/lint-tests.d.ts.map +1 -0
- package/dist/commands/lint-tests.js +105 -0
- package/dist/commands/lint-tests.js.map +1 -0
- package/dist/commands/lint-workflow.d.ts +36 -0
- package/dist/commands/lint-workflow.d.ts.map +1 -0
- package/dist/commands/lint-workflow.js +255 -0
- package/dist/commands/lint-workflow.js.map +1 -0
- package/dist/commands/overview.d.ts +21 -0
- package/dist/commands/overview.d.ts.map +1 -0
- package/dist/commands/overview.js +1300 -0
- package/dist/commands/overview.js.map +1 -0
- package/dist/commands/packages.d.ts +107 -0
- package/dist/commands/packages.d.ts.map +1 -0
- package/dist/commands/packages.js +308 -0
- package/dist/commands/packages.js.map +1 -0
- package/dist/commands/portal-nextjs.d.ts +23 -0
- package/dist/commands/portal-nextjs.d.ts.map +1 -0
- package/dist/commands/portal-nextjs.js +336 -0
- package/dist/commands/portal-nextjs.js.map +1 -0
- package/dist/commands/portal.d.ts +24 -0
- package/dist/commands/portal.d.ts.map +1 -0
- package/dist/commands/portal.js +16 -0
- package/dist/commands/portal.js.map +1 -0
- package/dist/commands/projects.d.ts +54 -0
- package/dist/commands/projects.d.ts.map +1 -0
- package/dist/commands/projects.js +969 -0
- package/dist/commands/projects.js.map +1 -0
- package/dist/commands/repo-pairs.d.ts +19 -0
- package/dist/commands/repo-pairs.d.ts.map +1 -0
- package/dist/commands/repo-pairs.js +529 -0
- package/dist/commands/repo-pairs.js.map +1 -0
- package/dist/commands/repo.d.ts +18 -0
- package/dist/commands/repo.d.ts.map +1 -0
- package/dist/commands/repo.js +253 -0
- package/dist/commands/repo.js.map +1 -0
- package/dist/commands/schema.d.ts +49 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +830 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/screenshots.d.ts +203 -0
- package/dist/commands/screenshots.d.ts.map +1 -0
- package/dist/commands/screenshots.js +1234 -0
- package/dist/commands/screenshots.js.map +1 -0
- package/dist/commands/search-index.d.ts +83 -0
- package/dist/commands/search-index.d.ts.map +1 -0
- package/dist/commands/search-index.js +389 -0
- package/dist/commands/search-index.js.map +1 -0
- package/dist/commands/session.d.ts +153 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +1243 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/test-cases-types.d.ts +154 -0
- package/dist/commands/test-cases-types.d.ts.map +1 -0
- package/dist/commands/test-cases-types.js +7 -0
- package/dist/commands/test-cases-types.js.map +1 -0
- package/dist/commands/test-cases.d.ts +28 -0
- package/dist/commands/test-cases.d.ts.map +1 -0
- package/dist/commands/test-cases.js +192 -0
- package/dist/commands/test-cases.js.map +1 -0
- package/dist/commands/typedoc.d.ts +21 -0
- package/dist/commands/typedoc.d.ts.map +1 -0
- package/dist/commands/typedoc.js +192 -0
- package/dist/commands/typedoc.js.map +1 -0
- package/dist/commands/update-skills.d.ts +56 -0
- package/dist/commands/update-skills.d.ts.map +1 -0
- package/dist/commands/update-skills.js +620 -0
- package/dist/commands/update-skills.js.map +1 -0
- package/dist/generators/details-entity-pages.d.ts +40 -0
- package/dist/generators/details-entity-pages.d.ts.map +1 -0
- package/dist/generators/details-entity-pages.js +301 -0
- package/dist/generators/details-entity-pages.js.map +1 -0
- package/dist/generators/details-html.d.ts +23 -0
- package/dist/generators/details-html.d.ts.map +1 -0
- package/dist/generators/details-html.js +324 -0
- package/dist/generators/details-html.js.map +1 -0
- package/dist/generators/details-module-page.d.ts +33 -0
- package/dist/generators/details-module-page.d.ts.map +1 -0
- package/dist/generators/details-module-page.js +408 -0
- package/dist/generators/details-module-page.js.map +1 -0
- package/dist/generators/details-styles.d.ts +39 -0
- package/dist/generators/details-styles.d.ts.map +1 -0
- package/dist/generators/details-styles.js +409 -0
- package/dist/generators/details-styles.js.map +1 -0
- package/dist/generators/feature-map-html.d.ts +66 -0
- package/dist/generators/feature-map-html.d.ts.map +1 -0
- package/dist/generators/feature-map-html.js +569 -0
- package/dist/generators/feature-map-html.js.map +1 -0
- package/dist/generators/feature-map-styles.d.ts +39 -0
- package/dist/generators/feature-map-styles.d.ts.map +1 -0
- package/dist/generators/feature-map-styles.js +449 -0
- package/dist/generators/feature-map-styles.js.map +1 -0
- package/dist/generators/test-cases-hierarchy.d.ts +21 -0
- package/dist/generators/test-cases-hierarchy.d.ts.map +1 -0
- package/dist/generators/test-cases-hierarchy.js +336 -0
- package/dist/generators/test-cases-hierarchy.js.map +1 -0
- package/dist/generators/test-cases-main.d.ts +20 -0
- package/dist/generators/test-cases-main.d.ts.map +1 -0
- package/dist/generators/test-cases-main.js +439 -0
- package/dist/generators/test-cases-main.js.map +1 -0
- package/dist/generators/test-cases-styles.d.ts +64 -0
- package/dist/generators/test-cases-styles.d.ts.map +1 -0
- package/dist/generators/test-cases-styles.js +1277 -0
- package/dist/generators/test-cases-styles.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +517 -0
- package/dist/index.js.map +1 -0
- package/dist/lint/annotation-lint.d.ts +198 -0
- package/dist/lint/annotation-lint.d.ts.map +1 -0
- package/dist/lint/annotation-lint.js +510 -0
- package/dist/lint/annotation-lint.js.map +1 -0
- package/dist/lint/annotation-types.d.ts +161 -0
- package/dist/lint/annotation-types.d.ts.map +1 -0
- package/dist/lint/annotation-types.js +31 -0
- package/dist/lint/annotation-types.js.map +1 -0
- package/dist/lint/code-types.d.ts +135 -0
- package/dist/lint/code-types.d.ts.map +1 -0
- package/dist/lint/code-types.js +25 -0
- package/dist/lint/code-types.js.map +1 -0
- package/dist/lint/coverage-types.d.ts +128 -0
- package/dist/lint/coverage-types.d.ts.map +1 -0
- package/dist/lint/coverage-types.js +24 -0
- package/dist/lint/coverage-types.js.map +1 -0
- package/dist/lint/docs-types.d.ts +214 -0
- package/dist/lint/docs-types.d.ts.map +1 -0
- package/dist/lint/docs-types.js +18 -0
- package/dist/lint/docs-types.js.map +1 -0
- package/dist/lint/formatters/index.d.ts +14 -0
- package/dist/lint/formatters/index.d.ts.map +1 -0
- package/dist/lint/formatters/index.js +28 -0
- package/dist/lint/formatters/index.js.map +1 -0
- package/dist/lint/formatters/json.d.ts +11 -0
- package/dist/lint/formatters/json.d.ts.map +1 -0
- package/dist/lint/formatters/json.js +12 -0
- package/dist/lint/formatters/json.js.map +1 -0
- package/dist/lint/formatters/summary.d.ts +11 -0
- package/dist/lint/formatters/summary.d.ts.map +1 -0
- package/dist/lint/formatters/summary.js +37 -0
- package/dist/lint/formatters/summary.js.map +1 -0
- package/dist/lint/formatters/terminal.d.ts +11 -0
- package/dist/lint/formatters/terminal.d.ts.map +1 -0
- package/dist/lint/formatters/terminal.js +99 -0
- package/dist/lint/formatters/terminal.js.map +1 -0
- package/dist/lint/index.d.ts +18 -0
- package/dist/lint/index.d.ts.map +1 -0
- package/dist/lint/index.js +103 -0
- package/dist/lint/index.js.map +1 -0
- package/dist/lint/rules/annotation-required.d.ts +35 -0
- package/dist/lint/rules/annotation-required.d.ts.map +1 -0
- package/dist/lint/rules/annotation-required.js +127 -0
- package/dist/lint/rules/annotation-required.js.map +1 -0
- package/dist/lint/rules/code-rules.d.ts +12 -0
- package/dist/lint/rules/code-rules.d.ts.map +1 -0
- package/dist/lint/rules/code-rules.js +11 -0
- package/dist/lint/rules/code-rules.js.map +1 -0
- package/dist/lint/rules/describe-coverage.d.ts +8 -0
- package/dist/lint/rules/describe-coverage.d.ts.map +1 -0
- package/dist/lint/rules/describe-coverage.js +43 -0
- package/dist/lint/rules/describe-coverage.js.map +1 -0
- package/dist/lint/rules/duplicate-testdoc.d.ts +8 -0
- package/dist/lint/rules/duplicate-testdoc.d.ts.map +1 -0
- package/dist/lint/rules/duplicate-testdoc.js +38 -0
- package/dist/lint/rules/duplicate-testdoc.js.map +1 -0
- package/dist/lint/rules/index.d.ts +29 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lint/rules/index.js +55 -0
- package/dist/lint/rules/index.js.map +1 -0
- package/dist/lint/rules/server-action-structure.d.ts +37 -0
- package/dist/lint/rules/server-action-structure.d.ts.map +1 -0
- package/dist/lint/rules/server-action-structure.js +151 -0
- package/dist/lint/rules/server-action-structure.js.map +1 -0
- package/dist/lint/rules/skipped-test-report.d.ts +11 -0
- package/dist/lint/rules/skipped-test-report.d.ts.map +1 -0
- package/dist/lint/rules/skipped-test-report.js +31 -0
- package/dist/lint/rules/skipped-test-report.js.map +1 -0
- package/dist/lint/rules/structure-rules.d.ts +67 -0
- package/dist/lint/rules/structure-rules.d.ts.map +1 -0
- package/dist/lint/rules/structure-rules.js +615 -0
- package/dist/lint/rules/structure-rules.js.map +1 -0
- package/dist/lint/rules/testdoc-japanese.d.ts +8 -0
- package/dist/lint/rules/testdoc-japanese.d.ts.map +1 -0
- package/dist/lint/rules/testdoc-japanese.js +31 -0
- package/dist/lint/rules/testdoc-japanese.js.map +1 -0
- package/dist/lint/rules/testdoc-min-length.d.ts +8 -0
- package/dist/lint/rules/testdoc-min-length.d.ts.map +1 -0
- package/dist/lint/rules/testdoc-min-length.js +31 -0
- package/dist/lint/rules/testdoc-min-length.js.map +1 -0
- package/dist/lint/rules/testdoc-required.d.ts +8 -0
- package/dist/lint/rules/testdoc-required.d.ts.map +1 -0
- package/dist/lint/rules/testdoc-required.js +27 -0
- package/dist/lint/rules/testdoc-required.js.map +1 -0
- package/dist/lint/rules/workflow-branch-naming.d.ts +20 -0
- package/dist/lint/rules/workflow-branch-naming.d.ts.map +1 -0
- package/dist/lint/rules/workflow-branch-naming.js +85 -0
- package/dist/lint/rules/workflow-branch-naming.js.map +1 -0
- package/dist/lint/rules/workflow-commit-format.d.ts +27 -0
- package/dist/lint/rules/workflow-commit-format.d.ts.map +1 -0
- package/dist/lint/rules/workflow-commit-format.js +92 -0
- package/dist/lint/rules/workflow-commit-format.js.map +1 -0
- package/dist/lint/rules/workflow-issue-fields.d.ts +24 -0
- package/dist/lint/rules/workflow-issue-fields.d.ts.map +1 -0
- package/dist/lint/rules/workflow-issue-fields.js +89 -0
- package/dist/lint/rules/workflow-issue-fields.js.map +1 -0
- package/dist/lint/rules/workflow-main-protection.d.ts +32 -0
- package/dist/lint/rules/workflow-main-protection.d.ts.map +1 -0
- package/dist/lint/rules/workflow-main-protection.js +114 -0
- package/dist/lint/rules/workflow-main-protection.js.map +1 -0
- package/dist/lint/structure-types.d.ts +216 -0
- package/dist/lint/structure-types.d.ts.map +1 -0
- package/dist/lint/structure-types.js +96 -0
- package/dist/lint/structure-types.js.map +1 -0
- package/dist/lint/types.d.ts +154 -0
- package/dist/lint/types.d.ts.map +1 -0
- package/dist/lint/types.js +21 -0
- package/dist/lint/types.js.map +1 -0
- package/dist/lint/workflow-types.d.ts +90 -0
- package/dist/lint/workflow-types.d.ts.map +1 -0
- package/dist/lint/workflow-types.js +7 -0
- package/dist/lint/workflow-types.js.map +1 -0
- package/dist/md/analyzer/index.d.ts +46 -0
- package/dist/md/analyzer/index.d.ts.map +1 -0
- package/dist/md/analyzer/index.js +288 -0
- package/dist/md/analyzer/index.js.map +1 -0
- package/dist/md/builder/index.d.ts +91 -0
- package/dist/md/builder/index.d.ts.map +1 -0
- package/dist/md/builder/index.js +446 -0
- package/dist/md/builder/index.js.map +1 -0
- package/dist/md/cli/analyze.d.ts +11 -0
- package/dist/md/cli/analyze.d.ts.map +1 -0
- package/dist/md/cli/analyze.js +118 -0
- package/dist/md/cli/analyze.js.map +1 -0
- package/dist/md/cli/build.d.ts +11 -0
- package/dist/md/cli/build.d.ts.map +1 -0
- package/dist/md/cli/build.js +74 -0
- package/dist/md/cli/build.js.map +1 -0
- package/dist/md/cli/extract.d.ts +25 -0
- package/dist/md/cli/extract.d.ts.map +1 -0
- package/dist/md/cli/extract.js +230 -0
- package/dist/md/cli/extract.js.map +1 -0
- package/dist/md/cli/index.d.ts +3 -0
- package/dist/md/cli/index.d.ts.map +1 -0
- package/dist/md/cli/index.js +99 -0
- package/dist/md/cli/index.js.map +1 -0
- package/dist/md/cli/lint.d.ts +11 -0
- package/dist/md/cli/lint.d.ts.map +1 -0
- package/dist/md/cli/lint.js +165 -0
- package/dist/md/cli/lint.js.map +1 -0
- package/dist/md/cli/list.d.ts +16 -0
- package/dist/md/cli/list.d.ts.map +1 -0
- package/dist/md/cli/list.js +85 -0
- package/dist/md/cli/list.js.map +1 -0
- package/dist/md/cli/program.d.ts +11 -0
- package/dist/md/cli/program.d.ts.map +1 -0
- package/dist/md/cli/program.js +104 -0
- package/dist/md/cli/program.js.map +1 -0
- package/dist/md/cli/validate.d.ts +8 -0
- package/dist/md/cli/validate.d.ts.map +1 -0
- package/dist/md/cli/validate.js +82 -0
- package/dist/md/cli/validate.js.map +1 -0
- package/dist/md/constants.d.ts +69 -0
- package/dist/md/constants.d.ts.map +1 -0
- package/dist/md/constants.js +69 -0
- package/dist/md/constants.js.map +1 -0
- package/dist/md/extractor/index.d.ts +57 -0
- package/dist/md/extractor/index.d.ts.map +1 -0
- package/dist/md/extractor/index.js +359 -0
- package/dist/md/extractor/index.js.map +1 -0
- package/dist/md/index.d.ts +26 -0
- package/dist/md/index.d.ts.map +1 -0
- package/dist/md/index.js +30 -0
- package/dist/md/index.js.map +1 -0
- package/dist/md/linter/index.d.ts +20 -0
- package/dist/md/linter/index.d.ts.map +1 -0
- package/dist/md/linter/index.js +412 -0
- package/dist/md/linter/index.js.map +1 -0
- package/dist/md/linter/token-optimizer.d.ts +66 -0
- package/dist/md/linter/token-optimizer.d.ts.map +1 -0
- package/dist/md/linter/token-optimizer.js +292 -0
- package/dist/md/linter/token-optimizer.js.map +1 -0
- package/dist/md/lister/index.d.ts +42 -0
- package/dist/md/lister/index.d.ts.map +1 -0
- package/dist/md/lister/index.js +317 -0
- package/dist/md/lister/index.js.map +1 -0
- package/dist/md/parser/heading-numbers.d.ts +43 -0
- package/dist/md/parser/heading-numbers.d.ts.map +1 -0
- package/dist/md/parser/heading-numbers.js +97 -0
- package/dist/md/parser/heading-numbers.js.map +1 -0
- package/dist/md/parser/section-meta.d.ts +50 -0
- package/dist/md/parser/section-meta.d.ts.map +1 -0
- package/dist/md/parser/section-meta.js +212 -0
- package/dist/md/parser/section-meta.js.map +1 -0
- package/dist/md/parser/template.d.ts +56 -0
- package/dist/md/parser/template.d.ts.map +1 -0
- package/dist/md/parser/template.js +122 -0
- package/dist/md/parser/template.js.map +1 -0
- package/dist/md/plugins/loader.d.ts +15 -0
- package/dist/md/plugins/loader.d.ts.map +1 -0
- package/dist/md/plugins/loader.js +80 -0
- package/dist/md/plugins/loader.js.map +1 -0
- package/dist/md/plugins/normalize-headings.d.ts +43 -0
- package/dist/md/plugins/normalize-headings.d.ts.map +1 -0
- package/dist/md/plugins/normalize-headings.js +51 -0
- package/dist/md/plugins/normalize-headings.js.map +1 -0
- package/dist/md/plugins/normalize-whitespace.d.ts +46 -0
- package/dist/md/plugins/normalize-whitespace.d.ts.map +1 -0
- package/dist/md/plugins/normalize-whitespace.js +86 -0
- package/dist/md/plugins/normalize-whitespace.js.map +1 -0
- package/dist/md/plugins/remove-badges.d.ts +36 -0
- package/dist/md/plugins/remove-badges.d.ts.map +1 -0
- package/dist/md/plugins/remove-badges.js +59 -0
- package/dist/md/plugins/remove-badges.js.map +1 -0
- package/dist/md/plugins/remove-comments.d.ts +27 -0
- package/dist/md/plugins/remove-comments.d.ts.map +1 -0
- package/dist/md/plugins/remove-comments.js +40 -0
- package/dist/md/plugins/remove-comments.js.map +1 -0
- package/dist/md/plugins/remove-duplicates.d.ts +40 -0
- package/dist/md/plugins/remove-duplicates.d.ts.map +1 -0
- package/dist/md/plugins/remove-duplicates.js +72 -0
- package/dist/md/plugins/remove-duplicates.js.map +1 -0
- package/dist/md/plugins/remove-internal-links.d.ts +38 -0
- package/dist/md/plugins/remove-internal-links.d.ts.map +1 -0
- package/dist/md/plugins/remove-internal-links.js +66 -0
- package/dist/md/plugins/remove-internal-links.js.map +1 -0
- package/dist/md/plugins/strip-heading-numbers.d.ts +35 -0
- package/dist/md/plugins/strip-heading-numbers.d.ts.map +1 -0
- package/dist/md/plugins/strip-heading-numbers.js +59 -0
- package/dist/md/plugins/strip-heading-numbers.js.map +1 -0
- package/dist/md/plugins/strip-section-meta.d.ts +37 -0
- package/dist/md/plugins/strip-section-meta.d.ts.map +1 -0
- package/dist/md/plugins/strip-section-meta.js +62 -0
- package/dist/md/plugins/strip-section-meta.js.map +1 -0
- package/dist/md/types/config.d.ts +260 -0
- package/dist/md/types/config.d.ts.map +1 -0
- package/dist/md/types/config.js +156 -0
- package/dist/md/types/config.js.map +1 -0
- package/dist/md/types/document.d.ts +37 -0
- package/dist/md/types/document.d.ts.map +1 -0
- package/dist/md/types/document.js +2 -0
- package/dist/md/types/document.js.map +1 -0
- package/dist/md/types/validation.d.ts +107 -0
- package/dist/md/types/validation.d.ts.map +1 -0
- package/dist/md/types/validation.js +2 -0
- package/dist/md/types/validation.js.map +1 -0
- package/dist/md/utils/code-blocks.d.ts +136 -0
- package/dist/md/utils/code-blocks.d.ts.map +1 -0
- package/dist/md/utils/code-blocks.js +178 -0
- package/dist/md/utils/code-blocks.js.map +1 -0
- package/dist/md/utils/config.d.ts +10 -0
- package/dist/md/utils/config.d.ts.map +1 -0
- package/dist/md/utils/config.js +99 -0
- package/dist/md/utils/config.js.map +1 -0
- package/dist/md/utils/file-collector.d.ts +78 -0
- package/dist/md/utils/file-collector.d.ts.map +1 -0
- package/dist/md/utils/file-collector.js +100 -0
- package/dist/md/utils/file-collector.js.map +1 -0
- package/dist/md/utils/markdown.d.ts +18 -0
- package/dist/md/utils/markdown.d.ts.map +1 -0
- package/dist/md/utils/markdown.js +93 -0
- package/dist/md/utils/markdown.js.map +1 -0
- package/dist/md/utils/remark.d.ts +91 -0
- package/dist/md/utils/remark.d.ts.map +1 -0
- package/dist/md/utils/remark.js +125 -0
- package/dist/md/utils/remark.js.map +1 -0
- package/dist/md/utils/tokens.d.ts +9 -0
- package/dist/md/utils/tokens.d.ts.map +1 -0
- package/dist/md/utils/tokens.js +31 -0
- package/dist/md/utils/tokens.js.map +1 -0
- package/dist/md/validator/index.d.ts +40 -0
- package/dist/md/validator/index.d.ts.map +1 -0
- package/dist/md/validator/index.js +289 -0
- package/dist/md/validator/index.js.map +1 -0
- package/dist/parsers/details-jsdoc.d.ts +46 -0
- package/dist/parsers/details-jsdoc.d.ts.map +1 -0
- package/dist/parsers/details-jsdoc.js +262 -0
- package/dist/parsers/details-jsdoc.js.map +1 -0
- package/dist/parsers/details-zod.d.ts +22 -0
- package/dist/parsers/details-zod.d.ts.map +1 -0
- package/dist/parsers/details-zod.js +145 -0
- package/dist/parsers/details-zod.js.map +1 -0
- package/dist/parsers/drizzle-schema.d.ts +92 -0
- package/dist/parsers/drizzle-schema.d.ts.map +1 -0
- package/dist/parsers/drizzle-schema.js +376 -0
- package/dist/parsers/drizzle-schema.js.map +1 -0
- package/dist/parsers/feature-map-tags.d.ts +45 -0
- package/dist/parsers/feature-map-tags.d.ts.map +1 -0
- package/dist/parsers/feature-map-tags.js +292 -0
- package/dist/parsers/feature-map-tags.js.map +1 -0
- package/dist/parsers/feature-map-type-extraction.d.ts +62 -0
- package/dist/parsers/feature-map-type-extraction.d.ts.map +1 -0
- package/dist/parsers/feature-map-type-extraction.js +347 -0
- package/dist/parsers/feature-map-type-extraction.js.map +1 -0
- package/dist/parsers/feature-map-utils.d.ts +34 -0
- package/dist/parsers/feature-map-utils.d.ts.map +1 -0
- package/dist/parsers/feature-map-utils.js +101 -0
- package/dist/parsers/feature-map-utils.js.map +1 -0
- package/dist/parsers/jsdoc-common.d.ts +209 -0
- package/dist/parsers/jsdoc-common.d.ts.map +1 -0
- package/dist/parsers/jsdoc-common.js +655 -0
- package/dist/parsers/jsdoc-common.js.map +1 -0
- package/dist/parsers/jsdoc.d.ts +76 -0
- package/dist/parsers/jsdoc.d.ts.map +1 -0
- package/dist/parsers/jsdoc.js +238 -0
- package/dist/parsers/jsdoc.js.map +1 -0
- package/dist/parsers/screenshot-annotations.d.ts +96 -0
- package/dist/parsers/screenshot-annotations.d.ts.map +1 -0
- package/dist/parsers/screenshot-annotations.js +227 -0
- package/dist/parsers/screenshot-annotations.js.map +1 -0
- package/dist/parsers/test-annotations.d.ts +46 -0
- package/dist/parsers/test-annotations.d.ts.map +1 -0
- package/dist/parsers/test-annotations.js +393 -0
- package/dist/parsers/test-annotations.js.map +1 -0
- package/dist/parsers/test-categorization.d.ts +42 -0
- package/dist/parsers/test-categorization.d.ts.map +1 -0
- package/dist/parsers/test-categorization.js +182 -0
- package/dist/parsers/test-categorization.js.map +1 -0
- package/dist/parsers/zod-schema.d.ts +105 -0
- package/dist/parsers/zod-schema.d.ts.map +1 -0
- package/dist/parsers/zod-schema.js +270 -0
- package/dist/parsers/zod-schema.js.map +1 -0
- package/dist/utils/action-inference.d.ts +23 -0
- package/dist/utils/action-inference.d.ts.map +1 -0
- package/dist/utils/action-inference.js +36 -0
- package/dist/utils/action-inference.js.map +1 -0
- package/dist/utils/app-inference.d.ts +31 -0
- package/dist/utils/app-inference.d.ts.map +1 -0
- package/dist/utils/app-inference.js +41 -0
- package/dist/utils/app-inference.js.map +1 -0
- package/dist/utils/auto-infer.d.ts +93 -0
- package/dist/utils/auto-infer.d.ts.map +1 -0
- package/dist/utils/auto-infer.js +184 -0
- package/dist/utils/auto-infer.js.map +1 -0
- package/dist/utils/config.d.ts +709 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +504 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/file.d.ts +46 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +103 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/formatters.d.ts +111 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +164 -0
- package/dist/utils/formatters.js.map +1 -0
- package/dist/utils/gh-config.d.ts +99 -0
- package/dist/utils/gh-config.d.ts.map +1 -0
- package/dist/utils/gh-config.js +247 -0
- package/dist/utils/gh-config.js.map +1 -0
- package/dist/utils/github.d.ts +98 -0
- package/dist/utils/github.d.ts.map +1 -0
- package/dist/utils/github.js +295 -0
- package/dist/utils/github.js.map +1 -0
- package/dist/utils/html.d.ts +107 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +376 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/i18n.d.ts +40 -0
- package/dist/utils/i18n.d.ts.map +1 -0
- package/dist/utils/i18n.js +148 -0
- package/dist/utils/i18n.js.map +1 -0
- package/dist/utils/logger.d.ts +20 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +49 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/project-fields.d.ts +71 -0
- package/dist/utils/project-fields.d.ts.map +1 -0
- package/dist/utils/project-fields.js +318 -0
- package/dist/utils/project-fields.js.map +1 -0
- package/dist/utils/repo-pairs.d.ts +94 -0
- package/dist/utils/repo-pairs.d.ts.map +1 -0
- package/dist/utils/repo-pairs.js +196 -0
- package/dist/utils/repo-pairs.js.map +1 -0
- package/dist/utils/route-inference.d.ts +81 -0
- package/dist/utils/route-inference.d.ts.map +1 -0
- package/dist/utils/route-inference.js +137 -0
- package/dist/utils/route-inference.js.map +1 -0
- package/dist/utils/setup-check.d.ts +34 -0
- package/dist/utils/setup-check.d.ts.map +1 -0
- package/dist/utils/setup-check.js +136 -0
- package/dist/utils/setup-check.js.map +1 -0
- package/dist/utils/shirokumaignore.d.ts +55 -0
- package/dist/utils/shirokumaignore.d.ts.map +1 -0
- package/dist/utils/shirokumaignore.js +94 -0
- package/dist/utils/shirokumaignore.js.map +1 -0
- package/dist/utils/skills-repo.d.ts +353 -0
- package/dist/utils/skills-repo.d.ts.map +1 -0
- package/dist/utils/skills-repo.js +793 -0
- package/dist/utils/skills-repo.js.map +1 -0
- package/dist/utils/status-workflow.d.ts +54 -0
- package/dist/utils/status-workflow.d.ts.map +1 -0
- package/dist/utils/status-workflow.js +103 -0
- package/dist/utils/status-workflow.js.map +1 -0
- package/dist/validators/frontmatter.d.ts +41 -0
- package/dist/validators/frontmatter.d.ts.map +1 -0
- package/dist/validators/frontmatter.js +117 -0
- package/dist/validators/frontmatter.js.map +1 -0
- package/dist/validators/link-checker.d.ts +48 -0
- package/dist/validators/link-checker.d.ts.map +1 -0
- package/dist/validators/link-checker.js +108 -0
- package/dist/validators/link-checker.js.map +1 -0
- package/dist/validators/markdown-structure.d.ts +50 -0
- package/dist/validators/markdown-structure.d.ts.map +1 -0
- package/dist/validators/markdown-structure.js +253 -0
- package/dist/validators/markdown-structure.js.map +1 -0
- package/i18n/cli/en.json +155 -0
- package/i18n/cli/ja.json +155 -0
- package/i18n/discussion/en.json +191 -0
- package/i18n/discussion/ja.json +191 -0
- package/package.json +113 -0
- package/portal/app/api-tools/api-tools-client.tsx +411 -0
- package/portal/app/api-tools/api-tools-document.tsx +240 -0
- package/portal/app/api-tools/page.tsx +56 -0
- package/portal/app/api-tools/swagger-view.tsx +114 -0
- package/portal/app/apps/[appId]/[type]/[module]/[item]/item-tabs-client.tsx +71 -0
- package/portal/app/apps/[appId]/[type]/[module]/[item]/page.tsx +1422 -0
- package/portal/app/apps/[appId]/[type]/[module]/page.tsx +373 -0
- package/portal/app/apps/[appId]/feature-map/feature-map-app-document.tsx +298 -0
- package/portal/app/apps/[appId]/feature-map/page.tsx +224 -0
- package/portal/app/apps/[appId]/i18n/page.tsx +139 -0
- package/portal/app/apps/[appId]/test-cases/page.tsx +840 -0
- package/portal/app/apps/[appId]/tools/[tool]/page.tsx +351 -0
- package/portal/app/apps/[appId]/tools/api-tools-client.tsx +429 -0
- package/portal/app/apps/[appId]/tools/page.tsx +119 -0
- package/portal/app/db-schema/[db]/[table]/page.tsx +235 -0
- package/portal/app/db-schema/[db]/diagram/page.tsx +81 -0
- package/portal/app/db-schema/[db]/page.tsx +148 -0
- package/portal/app/db-schema/db-schema-document.tsx +100 -0
- package/portal/app/db-schema/diagram/client.tsx +211 -0
- package/portal/app/db-schema/diagram/page.tsx +20 -0
- package/portal/app/db-schema/page.tsx +145 -0
- package/portal/app/db-schema/table-detail-document.tsx +710 -0
- package/portal/app/db-schema/table-detail.tsx +747 -0
- package/portal/app/db-schema/table-list-document.tsx +224 -0
- package/portal/app/db-schema/table-list.tsx +247 -0
- package/portal/app/details/[type]/[module]/[item]/item-tabs-client.tsx +71 -0
- package/portal/app/details/[type]/[module]/[item]/page.tsx +1286 -0
- package/portal/app/details/[type]/[module]/page.tsx +884 -0
- package/portal/app/feature-map/feature-map-client.tsx +681 -0
- package/portal/app/feature-map/feature-map-document.tsx +313 -0
- package/portal/app/feature-map/page.tsx +438 -0
- package/portal/app/globals.css +205 -0
- package/portal/app/i18n/[...namespace]/page.tsx +190 -0
- package/portal/app/i18n/i18n-client.tsx +369 -0
- package/portal/app/i18n/page.tsx +339 -0
- package/portal/app/layout.tsx +37 -0
- package/portal/app/overview/page.tsx +65 -0
- package/portal/app/packages/[packageId]/page.tsx +201 -0
- package/portal/app/packages/page.tsx +148 -0
- package/portal/app/page.tsx +568 -0
- package/portal/app/test-cases/[file]/[line]/page.tsx +455 -0
- package/portal/app/test-cases/[file]/[line]/test-detail-document.tsx +335 -0
- package/portal/app/test-cases/[file]/page.tsx +323 -0
- package/portal/app/test-cases/[file]/test-file-document.tsx +335 -0
- package/portal/app/test-cases/page.tsx +546 -0
- package/portal/app/test-cases/test-cases-document.tsx +384 -0
- package/portal/components/code-block.tsx +57 -0
- package/portal/components/document/doc-params-table.tsx +71 -0
- package/portal/components/document/doc-section.tsx +133 -0
- package/portal/components/document/doc-table.tsx +119 -0
- package/portal/components/document/index.ts +9 -0
- package/portal/components/drawflow-er-diagram.tsx +607 -0
- package/portal/components/interactive-er-diagram.tsx +228 -0
- package/portal/components/layout/app-sidebar.tsx +490 -0
- package/portal/components/layout/er-sidebar.tsx +116 -0
- package/portal/components/layout/global-header.tsx +117 -0
- package/portal/components/layout/layout-content.tsx +48 -0
- package/portal/components/markdown-content.tsx +120 -0
- package/portal/components/mermaid-diagram.tsx +83 -0
- package/portal/components/reactflow-er-diagram.tsx +475 -0
- package/portal/components/search-dialog.tsx +268 -0
- package/portal/components/shared/coverage-score-bar.tsx +144 -0
- package/portal/components/swagger/endpoint-accordion.tsx +117 -0
- package/portal/components/swagger/index.ts +7 -0
- package/portal/components/swagger/method-badge.tsx +55 -0
- package/portal/components/swagger/params-table.tsx +78 -0
- package/portal/components/tabs-with-hash.tsx +43 -0
- package/portal/components/test/index.ts +2 -0
- package/portal/components/test/test-bdd-card.tsx +192 -0
- package/portal/components/test/test-matrix.tsx +242 -0
- package/portal/components/ui/accordion.tsx +66 -0
- package/portal/components/ui/badge.tsx +46 -0
- package/portal/components/ui/breadcrumb.tsx +109 -0
- package/portal/components/ui/button.tsx +62 -0
- package/portal/components/ui/card.tsx +92 -0
- package/portal/components/ui/collapsible.tsx +33 -0
- package/portal/components/ui/dialog.tsx +118 -0
- package/portal/components/ui/progress.tsx +28 -0
- package/portal/components/ui/scroll-area.tsx +58 -0
- package/portal/components/ui/sheet.tsx +139 -0
- package/portal/components/ui/table.tsx +116 -0
- package/portal/components/ui/tabs.tsx +66 -0
- package/portal/components.json +21 -0
- package/portal/lib/constants/test-categories.ts +186 -0
- package/portal/lib/data-loader.ts +1181 -0
- package/portal/lib/db-schema-utils.ts +182 -0
- package/portal/lib/format.ts +43 -0
- package/portal/lib/hooks/use-hash-tab.ts +144 -0
- package/portal/lib/path-utils.ts +25 -0
- package/portal/lib/search-index-generator.ts +214 -0
- package/portal/lib/search.ts +126 -0
- package/portal/lib/sidebar-context.tsx +111 -0
- package/portal/lib/types.ts +740 -0
- package/portal/lib/utils.ts +6 -0
- package/portal/next.config.ts +21 -0
- package/portal/package.json +45 -0
- package/portal/postcss.config.mjs +8 -0
- package/portal/tsconfig.json +41 -0
- package/portal/types/drawflow.d.ts +80 -0
- package/templates/README.md +73 -0
- package/templates/coverage.html +367 -0
- package/templates/dark-theme.css +443 -0
- package/templates/discussion/adr.yml.hbs +65 -0
- package/templates/discussion/handovers.yml.hbs +57 -0
- package/templates/discussion/knowledge.yml.hbs +60 -0
- package/templates/discussion/reports.yml.hbs +68 -0
- package/templates/discussion/research.yml.hbs +61 -0
|
@@ -0,0 +1,1234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* screenshots コマンド - 画面スクリーンショット自動生成
|
|
3
|
+
*
|
|
4
|
+
* 画面情報を取得する方法:
|
|
5
|
+
* 1. annotations: page.tsx の @screenshot アノテーションからスキャン
|
|
6
|
+
* 2. feature-map: feature-map.json の screens から取得
|
|
7
|
+
* 3. config: 設定ファイルに直接定義された screens から取得
|
|
8
|
+
* 4. both: annotations と feature-map の両方を統合
|
|
9
|
+
*
|
|
10
|
+
* 機能:
|
|
11
|
+
* - 動的ルート ([locale], [orgSlug] 等) を設定値で置換
|
|
12
|
+
* - Playwright スクリプトを自動生成(scripts/screenshots/ に出力)
|
|
13
|
+
* - --run オプションで即時実行
|
|
14
|
+
*
|
|
15
|
+
* 出力先:
|
|
16
|
+
* - スクリプト: scripts/screenshots/capture-screens.playwright.ts
|
|
17
|
+
* - スクリーンショット: docs/portal/screenshots/
|
|
18
|
+
*
|
|
19
|
+
* 注意: E2Eテスト (tests/e2e/) とは別管理
|
|
20
|
+
*/
|
|
21
|
+
import { resolve, dirname, relative } from "node:path";
|
|
22
|
+
import { spawnSync } from "node:child_process";
|
|
23
|
+
import { globSync } from "glob";
|
|
24
|
+
import { loadConfig, getOutputPath } from "../utils/config.js";
|
|
25
|
+
import { ensureDir, writeFile, readFile, fileExists } from "../utils/file.js";
|
|
26
|
+
import { createLogger } from "../utils/logger.js";
|
|
27
|
+
import { parseScreenshotAnnotations } from "../parsers/screenshot-annotations.js";
|
|
28
|
+
import { inferRouteFromPath, applyRouteParams } from "../utils/route-inference.js";
|
|
29
|
+
/**
|
|
30
|
+
* パスからアプリ名を推測
|
|
31
|
+
* 例: "apps/admin/app/..." → "admin"
|
|
32
|
+
* "apps/public/app/..." → "public"
|
|
33
|
+
*/
|
|
34
|
+
function inferAppFromPath(path) {
|
|
35
|
+
if (!path)
|
|
36
|
+
return undefined;
|
|
37
|
+
const match = path.match(/^apps\/([^/]+)\//);
|
|
38
|
+
return match ? match[1] : undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* デフォルト設定
|
|
42
|
+
*/
|
|
43
|
+
const defaultScreenshotsConfig = {
|
|
44
|
+
enabled: true,
|
|
45
|
+
source: "feature-map",
|
|
46
|
+
scanPaths: ["apps/*/app/**/*page.tsx"],
|
|
47
|
+
screens: [],
|
|
48
|
+
baseUrl: "https://localhost:3000",
|
|
49
|
+
locale: "ja",
|
|
50
|
+
accounts: {},
|
|
51
|
+
defaultAccount: "",
|
|
52
|
+
loginPath: "/login",
|
|
53
|
+
auth: {
|
|
54
|
+
email: "admin@example.com",
|
|
55
|
+
password: "Admin@Test2024!",
|
|
56
|
+
loginPath: "/login",
|
|
57
|
+
},
|
|
58
|
+
viewport: {
|
|
59
|
+
width: 1280,
|
|
60
|
+
height: 720,
|
|
61
|
+
},
|
|
62
|
+
outputDir: "docs/portal/screenshots",
|
|
63
|
+
testFile: "scripts/screenshots/capture-screens.playwright.ts",
|
|
64
|
+
routeParams: {
|
|
65
|
+
"[locale]": "ja",
|
|
66
|
+
"[orgSlug]": "test-org",
|
|
67
|
+
"[projectSlug]": "test-project",
|
|
68
|
+
"[sessionId]": "test-session",
|
|
69
|
+
"[entityId]": "test-entity",
|
|
70
|
+
},
|
|
71
|
+
appBaseUrls: {},
|
|
72
|
+
screenOverrides: {},
|
|
73
|
+
dynamicRoutes: {
|
|
74
|
+
enabled: false,
|
|
75
|
+
helperModule: "./tests/helpers/database",
|
|
76
|
+
paramMethods: {},
|
|
77
|
+
databaseUrl: "",
|
|
78
|
+
},
|
|
79
|
+
apps: {},
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* アプリ別設定をマージして完全な設定を生成
|
|
83
|
+
*/
|
|
84
|
+
function getAppConfig(baseConfig, appId, appSettings) {
|
|
85
|
+
return {
|
|
86
|
+
...baseConfig,
|
|
87
|
+
baseUrl: appSettings.baseUrl ?? baseConfig.baseUrl,
|
|
88
|
+
testFile: appSettings.testFile ?? `scripts/screenshots/${appId}.playwright.ts`,
|
|
89
|
+
outputDir: appSettings.outputDir ?? `${baseConfig.outputDir}/${appId}`,
|
|
90
|
+
screenOverrides: { ...baseConfig.screenOverrides, ...appSettings.screenOverrides },
|
|
91
|
+
auth: appSettings.auth === null
|
|
92
|
+
? { email: "", password: "", loginPath: "" }
|
|
93
|
+
: appSettings.auth
|
|
94
|
+
? { ...baseConfig.auth, ...appSettings.auth }
|
|
95
|
+
: baseConfig.auth,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* screenshots コマンド
|
|
100
|
+
*/
|
|
101
|
+
export async function screenshotsCommand(options) {
|
|
102
|
+
const logger = createLogger(options.verbose ?? false);
|
|
103
|
+
const projectPath = resolve(options.project);
|
|
104
|
+
logger.info(`スクリーンショット生成テストを作成: ${projectPath}`);
|
|
105
|
+
// 設定読み込み
|
|
106
|
+
const config = loadConfig(projectPath, options.config);
|
|
107
|
+
const rawScreenshots = config.screenshots;
|
|
108
|
+
logger.debug(`Raw screenshots keys: ${JSON.stringify(Object.keys(rawScreenshots || {}))}`);
|
|
109
|
+
const screenshotsConfig = mergeScreenshotsConfig(defaultScreenshotsConfig, rawScreenshots);
|
|
110
|
+
logger.debug(`Merged screenOverrides: ${JSON.stringify(screenshotsConfig.screenOverrides)}`);
|
|
111
|
+
if (!screenshotsConfig.enabled) {
|
|
112
|
+
logger.warn("screenshots 機能は無効化されています");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// ソースに応じて画面を収集
|
|
116
|
+
let screens = [];
|
|
117
|
+
switch (screenshotsConfig.source) {
|
|
118
|
+
case "annotations":
|
|
119
|
+
screens = await collectFromAnnotations(projectPath, screenshotsConfig, logger);
|
|
120
|
+
break;
|
|
121
|
+
case "config":
|
|
122
|
+
screens = collectFromConfig(screenshotsConfig, logger);
|
|
123
|
+
break;
|
|
124
|
+
case "both":
|
|
125
|
+
const annotationScreens = await collectFromAnnotations(projectPath, screenshotsConfig, logger);
|
|
126
|
+
const featureMapScreens = await collectFromFeatureMap(projectPath, config, logger);
|
|
127
|
+
screens = mergeScreens(annotationScreens, featureMapScreens);
|
|
128
|
+
break;
|
|
129
|
+
case "feature-map":
|
|
130
|
+
default:
|
|
131
|
+
screens = await collectFromFeatureMap(projectPath, config, logger);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
logger.info(`${screens.length} 件の画面を検出 (source: ${screenshotsConfig.source})`);
|
|
135
|
+
if (screens.length === 0) {
|
|
136
|
+
logger.warn("スクリーンショット対象の画面が見つかりません");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// アプリ別設定があるかチェック
|
|
140
|
+
const hasAppConfigs = screenshotsConfig.apps && Object.keys(screenshotsConfig.apps).length > 0;
|
|
141
|
+
if (hasAppConfigs) {
|
|
142
|
+
// マルチアプリモード: アプリごとに別ファイル生成
|
|
143
|
+
await generateMultiAppScreenshots(screens, screenshotsConfig, projectPath, options, logger);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// シングルアプリモード: 従来の動作(後方互換)
|
|
147
|
+
await generateSingleAppScreenshots(screens, screenshotsConfig, projectPath, options, logger);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* シングルアプリモード: 従来の1ファイル生成
|
|
152
|
+
*/
|
|
153
|
+
async function generateSingleAppScreenshots(screens, screenshotsConfig, projectPath, options, logger) {
|
|
154
|
+
// Playwright スクリプト生成
|
|
155
|
+
const testContent = generateTestFile(screens, screenshotsConfig, projectPath, logger);
|
|
156
|
+
const testFilePath = resolve(projectPath, screenshotsConfig.testFile);
|
|
157
|
+
ensureDir(dirname(testFilePath));
|
|
158
|
+
writeFile(testFilePath, testContent);
|
|
159
|
+
logger.success(`スクリーンショットスクリプトを生成: ${testFilePath}`);
|
|
160
|
+
// 出力ディレクトリ作成
|
|
161
|
+
const outputDir = resolve(projectPath, screenshotsConfig.outputDir);
|
|
162
|
+
ensureDir(outputDir);
|
|
163
|
+
logger.debug(`スクリーンショット出力先: ${outputDir}`);
|
|
164
|
+
// マニフェスト生成(ポータル表示用)
|
|
165
|
+
const manifest = generateManifest(screens, screenshotsConfig, logger);
|
|
166
|
+
const manifestPath = resolve(outputDir, "screenshots.json");
|
|
167
|
+
writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
168
|
+
logger.success(`スクリーンショットマニフェストを生成: ${manifestPath}`);
|
|
169
|
+
// --run オプションで即時実行
|
|
170
|
+
if (options.run) {
|
|
171
|
+
logger.info("Playwright テストを実行中...");
|
|
172
|
+
const result = spawnSync("npx", ["playwright", "test", testFilePath, "--reporter=list"], {
|
|
173
|
+
cwd: projectPath,
|
|
174
|
+
stdio: "inherit",
|
|
175
|
+
shell: false,
|
|
176
|
+
});
|
|
177
|
+
if (result.status === 0) {
|
|
178
|
+
logger.success("スクリーンショット撮影が完了しました");
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
logger.error("Playwright テストの実行に失敗しました");
|
|
182
|
+
if (result.error) {
|
|
183
|
+
logger.debug(String(result.error));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
logger.info("スクリーンショットを撮影するには以下のコマンドを使用:");
|
|
189
|
+
logger.info(` npx playwright test ${screenshotsConfig.testFile}`);
|
|
190
|
+
logger.info("または --run オプションを付けて再実行してください");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* マルチアプリモード: アプリごとに別ファイル生成
|
|
195
|
+
*/
|
|
196
|
+
async function generateMultiAppScreenshots(allScreens, baseConfig, projectPath, options, logger) {
|
|
197
|
+
const appConfigs = baseConfig.apps;
|
|
198
|
+
const appIds = Object.keys(appConfigs);
|
|
199
|
+
logger.info(`マルチアプリモード: ${appIds.length} アプリ (${appIds.join(", ")})`);
|
|
200
|
+
// アプリごとにスクリーンを分類
|
|
201
|
+
const screensByApp = new Map();
|
|
202
|
+
const unassignedScreens = [];
|
|
203
|
+
for (const screen of allScreens) {
|
|
204
|
+
if (screen.app && appIds.includes(screen.app)) {
|
|
205
|
+
const existing = screensByApp.get(screen.app) || [];
|
|
206
|
+
existing.push(screen);
|
|
207
|
+
screensByApp.set(screen.app, existing);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
unassignedScreens.push(screen);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (unassignedScreens.length > 0) {
|
|
214
|
+
logger.warn(`${unassignedScreens.length} 件のスクリーンがアプリに紐付けられていません:`);
|
|
215
|
+
unassignedScreens.forEach(s => logger.warn(` - ${s.name} (path: ${s.path || "N/A"})`));
|
|
216
|
+
}
|
|
217
|
+
const generatedFiles = [];
|
|
218
|
+
const allManifests = {};
|
|
219
|
+
// 各アプリのファイルを生成
|
|
220
|
+
for (const appId of appIds) {
|
|
221
|
+
const appSettings = appConfigs[appId];
|
|
222
|
+
if (appSettings.enabled === false) {
|
|
223
|
+
logger.debug(`アプリ "${appId}" はスキップ (enabled: false)`);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const appScreens = screensByApp.get(appId) || [];
|
|
227
|
+
if (appScreens.length === 0) {
|
|
228
|
+
logger.warn(`アプリ "${appId}" にはスクリーンがありません`);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
logger.info(`\n📱 ${appId}: ${appScreens.length} 件のスクリーン`);
|
|
232
|
+
// アプリ固有の設定を生成
|
|
233
|
+
const appConfig = getAppConfig(baseConfig, appId, appSettings);
|
|
234
|
+
// Playwright スクリプト生成
|
|
235
|
+
const testContent = generateTestFileForApp(appScreens, appConfig, appId, projectPath, logger);
|
|
236
|
+
const testFilePath = resolve(projectPath, appConfig.testFile);
|
|
237
|
+
ensureDir(dirname(testFilePath));
|
|
238
|
+
writeFile(testFilePath, testContent);
|
|
239
|
+
logger.success(` スクリプト: ${testFilePath}`);
|
|
240
|
+
generatedFiles.push(testFilePath);
|
|
241
|
+
// 出力ディレクトリ作成
|
|
242
|
+
const outputDir = resolve(projectPath, appConfig.outputDir);
|
|
243
|
+
ensureDir(outputDir);
|
|
244
|
+
// マニフェスト生成
|
|
245
|
+
const manifest = generateManifest(appScreens, appConfig, logger);
|
|
246
|
+
const manifestPath = resolve(outputDir, "screenshots.json");
|
|
247
|
+
writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
248
|
+
logger.success(` マニフェスト: ${manifestPath}`);
|
|
249
|
+
allManifests[appId] = manifest;
|
|
250
|
+
}
|
|
251
|
+
// 統合マニフェスト生成(全アプリのインデックス)
|
|
252
|
+
const indexManifestPath = resolve(projectPath, baseConfig.outputDir, "index.json");
|
|
253
|
+
ensureDir(dirname(indexManifestPath));
|
|
254
|
+
writeFile(indexManifestPath, JSON.stringify({
|
|
255
|
+
generatedAt: new Date().toISOString(),
|
|
256
|
+
apps: Object.keys(allManifests),
|
|
257
|
+
manifests: allManifests,
|
|
258
|
+
}, null, 2));
|
|
259
|
+
logger.success(`\n統合マニフェスト: ${indexManifestPath}`);
|
|
260
|
+
// --run オプションで即時実行
|
|
261
|
+
if (options.run && generatedFiles.length > 0) {
|
|
262
|
+
logger.info("\nPlaywright テストを実行中...");
|
|
263
|
+
for (const testFile of generatedFiles) {
|
|
264
|
+
const relativePath = relative(projectPath, testFile);
|
|
265
|
+
logger.info(` 実行中: ${relativePath}`);
|
|
266
|
+
const result = spawnSync("npx", ["playwright", "test", testFile, "--reporter=list"], {
|
|
267
|
+
cwd: projectPath,
|
|
268
|
+
stdio: "inherit",
|
|
269
|
+
shell: false,
|
|
270
|
+
});
|
|
271
|
+
if (result.status !== 0) {
|
|
272
|
+
logger.error(` エラー: ${relativePath}`);
|
|
273
|
+
if (result.error) {
|
|
274
|
+
logger.debug(String(result.error));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
logger.success("全スクリーンショット撮影が完了しました");
|
|
279
|
+
}
|
|
280
|
+
else if (generatedFiles.length > 0) {
|
|
281
|
+
logger.info("\nスクリーンショットを撮影するには以下のコマンドを使用:");
|
|
282
|
+
for (const testFile of generatedFiles) {
|
|
283
|
+
const relativePath = relative(projectPath, testFile);
|
|
284
|
+
logger.info(` npx playwright test ${relativePath}`);
|
|
285
|
+
}
|
|
286
|
+
logger.info("\nまたは一括実行:");
|
|
287
|
+
logger.info(` npx playwright test scripts/screenshots/`);
|
|
288
|
+
logger.info("または --run オプションを付けて再実行してください");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* アプリ別テストファイルを生成
|
|
293
|
+
* 認証なし(auth: null)の場合はログインをスキップ
|
|
294
|
+
*/
|
|
295
|
+
function generateTestFileForApp(screens, config, appId, projectPath, logger) {
|
|
296
|
+
const tasks = expandScreensToTasks(screens, config, logger);
|
|
297
|
+
const dynamicRoutesEnabled = config.dynamicRoutes.enabled;
|
|
298
|
+
const requiresAuth = config.auth.email !== "";
|
|
299
|
+
logger.info(` ${tasks.length} 件のタスク (認証: ${requiresAuth ? "必要" : "不要"})${dynamicRoutesEnabled ? " [動的ルート有効]" : ""}`);
|
|
300
|
+
// 動的ルート解決用のインポート
|
|
301
|
+
let helperImportPath = config.dynamicRoutes.helperModule ?? "./tests/helpers/database";
|
|
302
|
+
if (dynamicRoutesEnabled && helperImportPath.startsWith("./")) {
|
|
303
|
+
const testFileDir = config.testFile.split("/").slice(0, -1).join("/");
|
|
304
|
+
const depth = testFileDir.split("/").filter(Boolean).length;
|
|
305
|
+
const prefix = "../".repeat(depth);
|
|
306
|
+
helperImportPath = prefix + helperImportPath.slice(2);
|
|
307
|
+
}
|
|
308
|
+
const dynamicRoutesImport = dynamicRoutesEnabled
|
|
309
|
+
? `import { testDb } from '${helperImportPath}';\n`
|
|
310
|
+
: "";
|
|
311
|
+
const dynamicRoutesSetupCode = dynamicRoutesEnabled
|
|
312
|
+
? `
|
|
313
|
+
// ===== 動的ルート解決: テストDBからエンティティIDを取得 =====
|
|
314
|
+
console.log('🔍 テストDBからエンティティIDを取得中...');
|
|
315
|
+
await testDb.connect();
|
|
316
|
+
const entityIds = await testDb.getScreenshotEntityIds();
|
|
317
|
+
await testDb.disconnect();
|
|
318
|
+
console.log('✅ エンティティID取得完了:', JSON.stringify(entityIds, null, 2));
|
|
319
|
+
|
|
320
|
+
const missingIds = Object.entries(entityIds)
|
|
321
|
+
.filter(([, value]) => value === null)
|
|
322
|
+
.map(([key]) => key);
|
|
323
|
+
if (missingIds.length > 0) {
|
|
324
|
+
console.warn('⚠️ 以下のエンティティIDが取得できませんでした:', missingIds.join(', '));
|
|
325
|
+
}
|
|
326
|
+
`
|
|
327
|
+
: "";
|
|
328
|
+
// ログインURL(baseUrl + loginPath)
|
|
329
|
+
const loginUrl = `${config.baseUrl}${config.auth.loginPath || "/login"}`;
|
|
330
|
+
// ログインコード(認証が必要な場合のみ)
|
|
331
|
+
const loginCode = requiresAuth
|
|
332
|
+
? `
|
|
333
|
+
// ===== ログイン =====
|
|
334
|
+
console.log('🔐 ログイン中...');
|
|
335
|
+
await page.goto('${loginUrl}');
|
|
336
|
+
await page.waitForLoadState('domcontentloaded');
|
|
337
|
+
|
|
338
|
+
await page.getByRole('textbox', { name: /Email|メールアドレス/i }).fill('${config.auth.email}');
|
|
339
|
+
await page.getByLabel(/^Password$|^パスワード$/i).fill('${config.auth.password}');
|
|
340
|
+
await page.getByRole('button', { name: /login|ログイン|Log in|Sign in/i }).click();
|
|
341
|
+
|
|
342
|
+
// ログイン成功を確認
|
|
343
|
+
await page.waitForURL(/\\/(${config.locale})?(\\/|$)/, { timeout: 15000 });
|
|
344
|
+
// セッションCookieの確立を待つ
|
|
345
|
+
await page.waitForLoadState('networkidle').catch(() => {});
|
|
346
|
+
await page.waitForTimeout(1000);
|
|
347
|
+
console.log('✅ ログイン成功');
|
|
348
|
+
`
|
|
349
|
+
: `
|
|
350
|
+
// ===== 認証不要モード =====
|
|
351
|
+
console.log('🔓 認証なしで撮影開始');
|
|
352
|
+
`;
|
|
353
|
+
// スクリーンショットステップを生成(baseUrlを渡す)
|
|
354
|
+
const screenshotSteps = tasks.map((task, index) => {
|
|
355
|
+
const isFirstInGroup = index === 0;
|
|
356
|
+
return generateScreenshotCodeForApp(task, config, index, isFirstInGroup, config.baseUrl);
|
|
357
|
+
});
|
|
358
|
+
return `/**
|
|
359
|
+
* Screen Screenshots for ${appId.toUpperCase()} App
|
|
360
|
+
*
|
|
361
|
+
* このファイルは shirokuma-docs screenshots コマンドで自動生成されました。
|
|
362
|
+
*
|
|
363
|
+
* 実行: npx playwright test ${config.testFile}
|
|
364
|
+
*
|
|
365
|
+
* 生成日時: ${new Date().toISOString()}
|
|
366
|
+
* アプリ: ${appId}
|
|
367
|
+
* スクリーンショット数: ${tasks.length}
|
|
368
|
+
* 認証: ${requiresAuth ? "必要" : "不要"}
|
|
369
|
+
*${dynamicRoutesEnabled ? " * 動的ルート: 有効\n *" : ""}
|
|
370
|
+
* @generated
|
|
371
|
+
*/
|
|
372
|
+
|
|
373
|
+
import { test, expect } from '@playwright/test';
|
|
374
|
+
${dynamicRoutesImport}
|
|
375
|
+
test.describe('${appId.toUpperCase()} Screenshots', () => {
|
|
376
|
+
test('Capture ${appId} screenshots', async ({ page }) => {
|
|
377
|
+
test.setTimeout(${tasks.length * 30000 + 60000});
|
|
378
|
+
|
|
379
|
+
await page.setViewportSize({
|
|
380
|
+
width: ${config.viewport.width},
|
|
381
|
+
height: ${config.viewport.height}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// ビューポートサイズインジケーターが消えるのを待つ
|
|
385
|
+
await page.waitForTimeout(2000);
|
|
386
|
+
${dynamicRoutesSetupCode}${loginCode}
|
|
387
|
+
console.log('📷 ${tasks.length}画面を撮影します...');
|
|
388
|
+
${screenshotSteps.join("\n")}
|
|
389
|
+
|
|
390
|
+
console.log('\\n✅ 全${tasks.length}件のスクリーンショット撮影完了');
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
`;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* 設定をマージ
|
|
397
|
+
*/
|
|
398
|
+
function mergeScreenshotsConfig(base, override) {
|
|
399
|
+
if (!override) {
|
|
400
|
+
return base;
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
enabled: override.enabled ?? base.enabled,
|
|
404
|
+
source: override.source ?? base.source,
|
|
405
|
+
scanPaths: override.scanPaths ?? base.scanPaths,
|
|
406
|
+
screens: override.screens ?? base.screens,
|
|
407
|
+
baseUrl: override.baseUrl ?? base.baseUrl,
|
|
408
|
+
locale: override.locale ?? base.locale,
|
|
409
|
+
accounts: override.accounts ?? base.accounts,
|
|
410
|
+
defaultAccount: override.defaultAccount ?? base.defaultAccount,
|
|
411
|
+
loginPath: override.loginPath ?? override.auth?.loginPath ?? base.loginPath,
|
|
412
|
+
auth: {
|
|
413
|
+
email: override.auth?.email ?? base.auth.email,
|
|
414
|
+
password: override.auth?.password ?? base.auth.password,
|
|
415
|
+
loginPath: override.auth?.loginPath ?? base.auth.loginPath,
|
|
416
|
+
},
|
|
417
|
+
viewport: {
|
|
418
|
+
width: override.viewport?.width ?? base.viewport.width,
|
|
419
|
+
height: override.viewport?.height ?? base.viewport.height,
|
|
420
|
+
},
|
|
421
|
+
outputDir: override.outputDir ?? base.outputDir,
|
|
422
|
+
testFile: override.testFile ?? base.testFile,
|
|
423
|
+
routeParams: { ...base.routeParams, ...override.routeParams },
|
|
424
|
+
appBaseUrls: { ...base.appBaseUrls, ...override.appBaseUrls },
|
|
425
|
+
screenOverrides: { ...base.screenOverrides, ...override.screenOverrides },
|
|
426
|
+
dynamicRoutes: {
|
|
427
|
+
enabled: override.dynamicRoutes?.enabled ?? base.dynamicRoutes.enabled,
|
|
428
|
+
helperModule: override.dynamicRoutes?.helperModule ?? base.dynamicRoutes.helperModule,
|
|
429
|
+
paramMethods: { ...base.dynamicRoutes.paramMethods, ...override.dynamicRoutes?.paramMethods },
|
|
430
|
+
databaseUrl: override.dynamicRoutes?.databaseUrl ?? base.dynamicRoutes.databaseUrl,
|
|
431
|
+
},
|
|
432
|
+
apps: override.apps ?? base.apps,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* アノテーションから画面を収集
|
|
437
|
+
*/
|
|
438
|
+
async function collectFromAnnotations(projectPath, config, logger) {
|
|
439
|
+
const screens = [];
|
|
440
|
+
for (const pattern of config.scanPaths) {
|
|
441
|
+
const files = globSync(pattern, {
|
|
442
|
+
cwd: projectPath,
|
|
443
|
+
absolute: true,
|
|
444
|
+
ignore: ["**/node_modules/**"],
|
|
445
|
+
});
|
|
446
|
+
logger.debug(`スキャンパターン: ${pattern} (${files.length} files)`);
|
|
447
|
+
for (const file of files) {
|
|
448
|
+
const content = readFile(file);
|
|
449
|
+
if (!content)
|
|
450
|
+
continue;
|
|
451
|
+
const relativePath = relative(projectPath, file);
|
|
452
|
+
const annotation = parseScreenshotAnnotations(content, relativePath);
|
|
453
|
+
if (annotation) {
|
|
454
|
+
// ルートを決定: @route アノテーション or ファイルパスから推論
|
|
455
|
+
const route = annotation.route || inferRouteFromPath(relativePath);
|
|
456
|
+
if (!route) {
|
|
457
|
+
logger.warn(`ルートを推論できませんでした: ${relativePath}`);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
screens.push({
|
|
461
|
+
name: annotation.name,
|
|
462
|
+
path: relativePath,
|
|
463
|
+
route,
|
|
464
|
+
description: annotation.description,
|
|
465
|
+
viewport: annotation.viewport,
|
|
466
|
+
auth: annotation.auth,
|
|
467
|
+
waitFor: annotation.waitFor,
|
|
468
|
+
waitForSelectors: annotation.waitForSelectors,
|
|
469
|
+
delay: annotation.delay,
|
|
470
|
+
accounts: annotation.accounts,
|
|
471
|
+
});
|
|
472
|
+
logger.debug(` @screenshot 検出: ${annotation.name} -> ${route}${annotation.accounts ? ` (accounts: ${annotation.accounts.join(", ")})` : ""}`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return screens;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* feature-map.json から画面を収集
|
|
480
|
+
*/
|
|
481
|
+
async function collectFromFeatureMap(projectPath, config, logger) {
|
|
482
|
+
const portalPath = getOutputPath(config, projectPath, "portal");
|
|
483
|
+
const featureMapPath = resolve(portalPath, "feature-map.json");
|
|
484
|
+
if (!fileExists(featureMapPath)) {
|
|
485
|
+
logger.warn(`feature-map.json が見つかりません: ${featureMapPath}`);
|
|
486
|
+
logger.info("先に shirokuma-docs feature-map を実行してください");
|
|
487
|
+
return [];
|
|
488
|
+
}
|
|
489
|
+
const featureMapContent = readFile(featureMapPath);
|
|
490
|
+
if (!featureMapContent) {
|
|
491
|
+
logger.error("feature-map.json の読み込みに失敗しました");
|
|
492
|
+
return [];
|
|
493
|
+
}
|
|
494
|
+
let featureMap;
|
|
495
|
+
try {
|
|
496
|
+
featureMap = JSON.parse(featureMapContent);
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
logger.error("feature-map.json のパースに失敗しました");
|
|
500
|
+
return [];
|
|
501
|
+
}
|
|
502
|
+
const screens = [];
|
|
503
|
+
// features からスクリーンを収集
|
|
504
|
+
for (const [, group] of Object.entries(featureMap.features)) {
|
|
505
|
+
if (group.screens && Array.isArray(group.screens)) {
|
|
506
|
+
for (const screen of group.screens) {
|
|
507
|
+
if (screen.route) {
|
|
508
|
+
screens.push({
|
|
509
|
+
name: screen.name,
|
|
510
|
+
path: screen.path,
|
|
511
|
+
route: screen.route,
|
|
512
|
+
description: screen.description,
|
|
513
|
+
app: inferAppFromPath(screen.path),
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// uncategorized からスクリーンを収集
|
|
520
|
+
if (featureMap.uncategorized?.screens) {
|
|
521
|
+
for (const screen of featureMap.uncategorized.screens) {
|
|
522
|
+
if (screen.route) {
|
|
523
|
+
screens.push({
|
|
524
|
+
name: screen.name,
|
|
525
|
+
path: screen.path,
|
|
526
|
+
route: screen.route,
|
|
527
|
+
description: screen.description,
|
|
528
|
+
app: inferAppFromPath(screen.path),
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// 重複を除去 (name ベース)
|
|
534
|
+
const uniqueScreens = new Map();
|
|
535
|
+
for (const screen of screens) {
|
|
536
|
+
if (!uniqueScreens.has(screen.name)) {
|
|
537
|
+
uniqueScreens.set(screen.name, screen);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return Array.from(uniqueScreens.values());
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* 設定ファイルから直接画面を取得
|
|
544
|
+
*/
|
|
545
|
+
function collectFromConfig(config, _logger) {
|
|
546
|
+
return config.screens.map((screen) => ({
|
|
547
|
+
name: screen.name,
|
|
548
|
+
route: screen.route,
|
|
549
|
+
description: screen.description,
|
|
550
|
+
viewport: screen.viewport,
|
|
551
|
+
auth: screen.auth,
|
|
552
|
+
waitFor: screen.waitFor,
|
|
553
|
+
delay: screen.delay,
|
|
554
|
+
}));
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* 複数ソースの画面をマージ (annotations が優先)
|
|
558
|
+
*/
|
|
559
|
+
function mergeScreens(annotationScreens, featureMapScreens) {
|
|
560
|
+
const merged = new Map();
|
|
561
|
+
// feature-map を先に追加
|
|
562
|
+
for (const screen of featureMapScreens) {
|
|
563
|
+
merged.set(screen.name, screen);
|
|
564
|
+
}
|
|
565
|
+
// annotations で上書き (優先)
|
|
566
|
+
for (const screen of annotationScreens) {
|
|
567
|
+
merged.set(screen.name, screen);
|
|
568
|
+
}
|
|
569
|
+
return Array.from(merged.values());
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* ルートパラメータを置換
|
|
573
|
+
* screenOverridesにスクリーン名が存在する場合はそちらを優先
|
|
574
|
+
*/
|
|
575
|
+
function resolveRoute(route, routeParams, screenName, screenOverrides) {
|
|
576
|
+
// screenOverridesに完全なルートが定義されていればそれを使用
|
|
577
|
+
if (screenName && screenOverrides && screenOverrides[screenName]) {
|
|
578
|
+
return screenOverrides[screenName];
|
|
579
|
+
}
|
|
580
|
+
return applyRouteParams(route, routeParams);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* マルチアカウント対応かどうかを判定
|
|
584
|
+
*/
|
|
585
|
+
function isMultiAccountMode(config) {
|
|
586
|
+
return Object.keys(config.accounts).length > 0;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* 有効なアカウント設定を取得(後方互換性対応)
|
|
590
|
+
*/
|
|
591
|
+
function getEffectiveAccounts(config) {
|
|
592
|
+
if (Object.keys(config.accounts).length > 0) {
|
|
593
|
+
return config.accounts;
|
|
594
|
+
}
|
|
595
|
+
// 後方互換: auth設定を "default" アカウントとして扱う
|
|
596
|
+
return {
|
|
597
|
+
default: {
|
|
598
|
+
email: config.auth.email ?? "admin@example.com",
|
|
599
|
+
password: config.auth.password ?? "Admin@Test2024!",
|
|
600
|
+
label: "Default",
|
|
601
|
+
},
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* デフォルトアカウント名を取得
|
|
606
|
+
*/
|
|
607
|
+
function getDefaultAccountName(config) {
|
|
608
|
+
if (config.defaultAccount && config.accounts[config.defaultAccount]) {
|
|
609
|
+
return config.defaultAccount;
|
|
610
|
+
}
|
|
611
|
+
const accountNames = Object.keys(getEffectiveAccounts(config));
|
|
612
|
+
return accountNames[0] || "default";
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* ログインパスを取得
|
|
616
|
+
*/
|
|
617
|
+
function getLoginPath(config) {
|
|
618
|
+
return config.loginPath || config.auth.loginPath || "/login";
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* スクリーンショットタスクを生成(アカウントごとに展開)
|
|
622
|
+
*/
|
|
623
|
+
function expandScreensToTasks(screens, config, logger) {
|
|
624
|
+
const tasks = [];
|
|
625
|
+
const accounts = getEffectiveAccounts(config);
|
|
626
|
+
const defaultAccountName = getDefaultAccountName(config);
|
|
627
|
+
const multiAccountMode = isMultiAccountMode(config);
|
|
628
|
+
for (const screen of screens) {
|
|
629
|
+
// 画面に指定されたアカウント、またはデフォルトアカウント
|
|
630
|
+
const targetAccounts = screen.accounts && screen.accounts.length > 0
|
|
631
|
+
? screen.accounts
|
|
632
|
+
: [defaultAccountName];
|
|
633
|
+
for (const accountName of targetAccounts) {
|
|
634
|
+
const accountConfig = accounts[accountName];
|
|
635
|
+
if (!accountConfig) {
|
|
636
|
+
logger.warn(`アカウント "${accountName}" が設定に存在しません (画面: ${screen.name})`);
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
// ファイル名: マルチアカウントかつ複数アカウント指定の場合はサフィックス付与
|
|
640
|
+
const needsSuffix = multiAccountMode && targetAccounts.length > 1;
|
|
641
|
+
const outputFileName = needsSuffix
|
|
642
|
+
? `${screen.name}-${accountName}.png`
|
|
643
|
+
: `${screen.name}.png`;
|
|
644
|
+
tasks.push({
|
|
645
|
+
screen,
|
|
646
|
+
accountName,
|
|
647
|
+
accountConfig,
|
|
648
|
+
outputFileName,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return tasks;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* タスクをアカウントごとにグループ化
|
|
656
|
+
*/
|
|
657
|
+
function groupTasksByAccount(tasks) {
|
|
658
|
+
const grouped = new Map();
|
|
659
|
+
for (const task of tasks) {
|
|
660
|
+
const existing = grouped.get(task.accountName) || [];
|
|
661
|
+
existing.push(task);
|
|
662
|
+
grouped.set(task.accountName, existing);
|
|
663
|
+
}
|
|
664
|
+
return grouped;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* スクリーンショットマニフェストを生成
|
|
668
|
+
*/
|
|
669
|
+
function generateManifest(screens, config, logger) {
|
|
670
|
+
const tasks = expandScreensToTasks(screens, config, logger);
|
|
671
|
+
const multiAccountMode = isMultiAccountMode(config);
|
|
672
|
+
const screenshots = {};
|
|
673
|
+
for (const task of tasks) {
|
|
674
|
+
const resolvedRoute = resolveRoute(task.screen.route, config.routeParams, task.screen.name, config.screenOverrides);
|
|
675
|
+
// マルチアカウントモードで複数アカウントがある場合はアカウント名をキーに含める
|
|
676
|
+
const key = multiAccountMode && task.screen.accounts && task.screen.accounts.length > 1
|
|
677
|
+
? `${task.screen.name}:${task.accountName}`
|
|
678
|
+
: task.screen.name;
|
|
679
|
+
const viewport = task.screen.viewport || {
|
|
680
|
+
width: config.viewport.width ?? 1280,
|
|
681
|
+
height: config.viewport.height ?? 720,
|
|
682
|
+
};
|
|
683
|
+
screenshots[key] = {
|
|
684
|
+
name: task.screen.name,
|
|
685
|
+
fileName: task.outputFileName,
|
|
686
|
+
route: resolvedRoute,
|
|
687
|
+
description: task.screen.description,
|
|
688
|
+
sourcePath: task.screen.path,
|
|
689
|
+
account: multiAccountMode ? task.accountName : undefined,
|
|
690
|
+
viewport,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
logger.debug(`マニフェスト生成: ${Object.keys(screenshots).length} エントリ`);
|
|
694
|
+
return {
|
|
695
|
+
generatedAt: new Date().toISOString(),
|
|
696
|
+
config: {
|
|
697
|
+
baseUrl: config.baseUrl,
|
|
698
|
+
viewport: {
|
|
699
|
+
width: config.viewport.width ?? 1280,
|
|
700
|
+
height: config.viewport.height ?? 720,
|
|
701
|
+
},
|
|
702
|
+
outputDir: config.outputDir,
|
|
703
|
+
},
|
|
704
|
+
screenshots,
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* 動的ルートを解決(テンプレートリテラル形式)
|
|
709
|
+
*
|
|
710
|
+
* screenOverridesにスクリーン名が存在する場合はそちらを優先
|
|
711
|
+
* dynamicRoutesが有効な場合は、[id]形式のパラメータを${entityIds.xxx}形式に変換
|
|
712
|
+
*/
|
|
713
|
+
function resolveRouteForDynamicMode(route, routeParams, screenName, screenOverrides, dynamicRoutes) {
|
|
714
|
+
// screenOverridesに完全なルートが定義されていればそれを使用(静的)
|
|
715
|
+
if (screenName && screenOverrides && screenOverrides[screenName]) {
|
|
716
|
+
// screenOverridesも動的パラメータを含む可能性があるのでチェック
|
|
717
|
+
const overrideRoute = screenOverrides[screenName];
|
|
718
|
+
if (dynamicRoutes.enabled) {
|
|
719
|
+
return convertToDynamicRoute(overrideRoute, dynamicRoutes);
|
|
720
|
+
}
|
|
721
|
+
return { route: overrideRoute, isDynamic: false };
|
|
722
|
+
}
|
|
723
|
+
// routeParamsで静的置換
|
|
724
|
+
let resolvedRoute = applyRouteParams(route, routeParams);
|
|
725
|
+
// dynamicRoutesが有効な場合は残りの[xxx]パターンを動的変数に変換
|
|
726
|
+
if (dynamicRoutes.enabled) {
|
|
727
|
+
return convertToDynamicRoute(resolvedRoute, dynamicRoutes);
|
|
728
|
+
}
|
|
729
|
+
return { route: resolvedRoute, isDynamic: false };
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* ルート内の[xxx]パターンを${entityIds.xxx}形式に変換
|
|
733
|
+
* ルートのパスからエンティティタイプを推論して適切なIDを使用
|
|
734
|
+
*/
|
|
735
|
+
function convertToDynamicRoute(route, dynamicRoutes) {
|
|
736
|
+
let isDynamic = false;
|
|
737
|
+
// カスタムマッピングがあれば使用
|
|
738
|
+
const paramMethods = dynamicRoutes.paramMethods || {};
|
|
739
|
+
// ルートからエンティティタイプを推論(例: /categories/[id]/edit -> "category")
|
|
740
|
+
const entityType = inferEntityTypeFromRoute(route);
|
|
741
|
+
// パターン: [id], [categoryId], [slug] など
|
|
742
|
+
const paramPattern = /\[([a-zA-Z]+)\]/g;
|
|
743
|
+
const convertedRoute = route.replace(paramPattern, (_match, paramName) => {
|
|
744
|
+
isDynamic = true;
|
|
745
|
+
// カスタムマッピングをチェック
|
|
746
|
+
const customMapping = paramMethods[`[${paramName}]`];
|
|
747
|
+
if (customMapping) {
|
|
748
|
+
return `\${entityIds.${customMapping}}`;
|
|
749
|
+
}
|
|
750
|
+
// 明示的な名前(categoryId, tagId等)はそのまま使用
|
|
751
|
+
const explicitMappings = {
|
|
752
|
+
"categoryId": "categoryId",
|
|
753
|
+
"tagId": "tagId",
|
|
754
|
+
"postId": "postId",
|
|
755
|
+
"userId": "userId",
|
|
756
|
+
"categorySlug": "categorySlug",
|
|
757
|
+
"tagSlug": "tagSlug",
|
|
758
|
+
"postSlug": "postSlug",
|
|
759
|
+
};
|
|
760
|
+
if (explicitMappings[paramName]) {
|
|
761
|
+
return `\${entityIds.${explicitMappings[paramName]}}`;
|
|
762
|
+
}
|
|
763
|
+
// [id] や [slug] の場合はルートからエンティティタイプを推論
|
|
764
|
+
if (paramName === "id" && entityType) {
|
|
765
|
+
return `\${entityIds.${entityType}Id}`;
|
|
766
|
+
}
|
|
767
|
+
if (paramName === "slug" && entityType) {
|
|
768
|
+
return `\${entityIds.${entityType}Slug}`;
|
|
769
|
+
}
|
|
770
|
+
// フォールバック: デフォルトマッピング
|
|
771
|
+
const fallbackMappings = {
|
|
772
|
+
"id": "categoryId",
|
|
773
|
+
"slug": "postSlug",
|
|
774
|
+
};
|
|
775
|
+
const mappedName = fallbackMappings[paramName] || paramName;
|
|
776
|
+
return `\${entityIds.${mappedName}}`;
|
|
777
|
+
});
|
|
778
|
+
return { route: convertedRoute, isDynamic };
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* ルートパスからエンティティタイプを推論
|
|
782
|
+
* 例: /categories/[id]/edit -> "category"
|
|
783
|
+
* /posts/[slug] -> "post"
|
|
784
|
+
*/
|
|
785
|
+
function inferEntityTypeFromRoute(route) {
|
|
786
|
+
// パスセグメントを抽出(ドメイン部分を除去)
|
|
787
|
+
const pathPart = route.replace(/^https?:\/\/[^/]+/, "");
|
|
788
|
+
// パターンマッチでエンティティタイプを推論
|
|
789
|
+
const patterns = [
|
|
790
|
+
{ pattern: /\/categories\//, type: "category" },
|
|
791
|
+
{ pattern: /\/category\//, type: "category" },
|
|
792
|
+
{ pattern: /\/tags\//, type: "tag" },
|
|
793
|
+
{ pattern: /\/tag\//, type: "tag" },
|
|
794
|
+
{ pattern: /\/posts\//, type: "post" },
|
|
795
|
+
{ pattern: /\/post\//, type: "post" },
|
|
796
|
+
{ pattern: /\/users\//, type: "user" },
|
|
797
|
+
{ pattern: /\/user\//, type: "user" },
|
|
798
|
+
];
|
|
799
|
+
for (const { pattern, type } of patterns) {
|
|
800
|
+
if (pattern.test(pathPart)) {
|
|
801
|
+
return type;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* スクリーンショット撮影コードを生成
|
|
808
|
+
*/
|
|
809
|
+
/**
|
|
810
|
+
* エラーチェックコードを生成
|
|
811
|
+
* 404/500エラー、Next.jsエラーページ、一般的なエラー表示、ログインリダイレクトを検出
|
|
812
|
+
*/
|
|
813
|
+
function generateErrorCheckCode(screenName, route) {
|
|
814
|
+
return `
|
|
815
|
+
// エラーチェック
|
|
816
|
+
{
|
|
817
|
+
const pageTitle = await page.title();
|
|
818
|
+
const h1Text = await page.locator('h1').first().textContent().catch(() => '');
|
|
819
|
+
const currentUrl = page.url();
|
|
820
|
+
const hasErrorIndicator =
|
|
821
|
+
pageTitle.includes('404') ||
|
|
822
|
+
pageTitle.includes('500') ||
|
|
823
|
+
pageTitle.toLowerCase().includes('error') ||
|
|
824
|
+
pageTitle.toLowerCase().includes('not found') ||
|
|
825
|
+
(h1Text && (
|
|
826
|
+
h1Text.includes('404') ||
|
|
827
|
+
h1Text.includes('500') ||
|
|
828
|
+
h1Text.toLowerCase().includes('error') ||
|
|
829
|
+
h1Text.toLowerCase().includes('not found') ||
|
|
830
|
+
h1Text.toLowerCase().includes('something went wrong')
|
|
831
|
+
));
|
|
832
|
+
|
|
833
|
+
// ログインページへのリダイレクト検出
|
|
834
|
+
const isLoginRedirect = currentUrl.includes('/login') || currentUrl.includes('/sign-in') ||
|
|
835
|
+
pageTitle.toLowerCase().includes('login') || pageTitle.toLowerCase().includes('sign in') ||
|
|
836
|
+
(h1Text && (
|
|
837
|
+
h1Text.toLowerCase().includes('login') ||
|
|
838
|
+
h1Text.toLowerCase().includes('sign in') ||
|
|
839
|
+
h1Text.toLowerCase().includes('log in') ||
|
|
840
|
+
h1Text.toLowerCase().includes('ログイン')
|
|
841
|
+
));
|
|
842
|
+
|
|
843
|
+
if (hasErrorIndicator) {
|
|
844
|
+
console.warn(' ⚠️ ${screenName}: エラーページを検出 (${route})');
|
|
845
|
+
console.warn(' Title: ' + pageTitle);
|
|
846
|
+
console.warn(' H1: ' + (h1Text || '(empty)'));
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if (isLoginRedirect && '${route}' !== '/login' && '${route}' !== '/sign-in') {
|
|
850
|
+
console.warn(' ⚠️ ${screenName}: ログインページにリダイレクトされました (${route})');
|
|
851
|
+
console.warn(' Current URL: ' + currentUrl);
|
|
852
|
+
console.warn(' 認証が切れている可能性があります');
|
|
853
|
+
}
|
|
854
|
+
}`;
|
|
855
|
+
}
|
|
856
|
+
function generateScreenshotCode(task, config, index, isFirstInGroup) {
|
|
857
|
+
const { screen, outputFileName } = task;
|
|
858
|
+
// 動的ルート解決(dynamicRoutes有効時はテンプレートリテラル形式)
|
|
859
|
+
const { route: resolvedRoute, isDynamic } = resolveRouteForDynamicMode(screen.route, config.routeParams, screen.name, config.screenOverrides, config.dynamicRoutes);
|
|
860
|
+
const screenshotPath = `${config.outputDir}/${outputFileName}`;
|
|
861
|
+
const waitFor = screen.waitFor || "networkidle";
|
|
862
|
+
const waitForSelectors = screen.waitForSelectors;
|
|
863
|
+
const delay = screen.delay ?? 500;
|
|
864
|
+
// セレクター待機コードを生成
|
|
865
|
+
const waitForSelectorsCode = waitForSelectors && waitForSelectors.length > 0
|
|
866
|
+
? waitForSelectors.map((selector) => ` await expect(page.locator('${selector.replace(/'/g, "\\'")}').first()).toBeVisible({ timeout: 10000 });`).join("\n") + "\n"
|
|
867
|
+
: "";
|
|
868
|
+
// ルート表示文字列(ログ用 - 動的な場合はプレースホルダーを含む)
|
|
869
|
+
const displayRoute = resolvedRoute.replace(/\$\{entityIds\.([a-zA-Z]+)\}/g, '{$1}');
|
|
870
|
+
// エラーチェックコードを生成
|
|
871
|
+
const errorCheckCode = generateErrorCheckCode(screen.name, displayRoute);
|
|
872
|
+
// gotoのURL(動的な場合はテンプレートリテラル、静的な場合は文字列)
|
|
873
|
+
const gotoUrl = isDynamic ? `\`${resolvedRoute}\`` : `'${resolvedRoute}'`;
|
|
874
|
+
// ダッシュボードルート(ロケールのみ)を判定
|
|
875
|
+
const dashboardRoutePattern = new RegExp(`^/${config.locale}/?$`);
|
|
876
|
+
const isDashboardRoute = !isDynamic && dashboardRoutePattern.test(resolvedRoute);
|
|
877
|
+
// ダッシュボードルートはログイン後すでにいるのでナビゲート不要(グループ内最初のスクリーンショット用)
|
|
878
|
+
if (isDashboardRoute && isFirstInGroup) {
|
|
879
|
+
return `
|
|
880
|
+
// === ${index + 1}. ${screen.name} ===
|
|
881
|
+
console.log(' [${index + 1}] ${screen.name} (${displayRoute})');
|
|
882
|
+
await page.waitForLoadState('load');
|
|
883
|
+
${waitForSelectorsCode} await page.waitForTimeout(1000);
|
|
884
|
+
// フォント読み込み完了とレイアウト安定化を待機
|
|
885
|
+
await page.evaluate(async () => {
|
|
886
|
+
await document.fonts.ready;
|
|
887
|
+
// レイアウト再計算を強制(offsetHeightの読み取りでreflow発生)
|
|
888
|
+
document.body.offsetHeight;
|
|
889
|
+
// 次のペイントサイクルを待つ
|
|
890
|
+
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
|
|
891
|
+
});
|
|
892
|
+
// 開発用オーバーレイを非表示(ビューポートインジケーター等)
|
|
893
|
+
await page.evaluate(() => {
|
|
894
|
+
document.querySelectorAll('[data-debug], [data-viewport], .tailwind-indicator, .viewport-indicator').forEach(el => {
|
|
895
|
+
(el as HTMLElement).style.display = 'none';
|
|
896
|
+
});
|
|
897
|
+
// 右下の解像度表示バッジを非表示(absolute bottom-* right-* のパターン)
|
|
898
|
+
document.querySelectorAll('.absolute.bottom-2.right-2, .absolute.bottom-4.right-4').forEach(el => {
|
|
899
|
+
const text = el.textContent || '';
|
|
900
|
+
if (/\\d+.*×.*\\d+/.test(text)) {
|
|
901
|
+
(el as HTMLElement).style.display = 'none';
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
});
|
|
905
|
+
${errorCheckCode}
|
|
906
|
+
await page.screenshot({
|
|
907
|
+
path: '${screenshotPath}',
|
|
908
|
+
fullPage: true
|
|
909
|
+
});`;
|
|
910
|
+
}
|
|
911
|
+
return `
|
|
912
|
+
// === ${index + 1}. ${screen.name} ===
|
|
913
|
+
console.log(' [${index + 1}] ${screen.name} (${isDynamic ? `' + ${gotoUrl} + '` : displayRoute})');
|
|
914
|
+
await page.goto(${gotoUrl}, { waitUntil: 'domcontentloaded' });
|
|
915
|
+
await page.waitForLoadState('${waitFor}', { timeout: 15000 }).catch(() => {
|
|
916
|
+
console.log(' ⚠️ ${waitFor}タイムアウト - 続行');
|
|
917
|
+
});
|
|
918
|
+
${waitForSelectorsCode} await page.waitForTimeout(${delay});
|
|
919
|
+
// フォント読み込み完了とレイアウト安定化を待機
|
|
920
|
+
await page.evaluate(async () => {
|
|
921
|
+
await document.fonts.ready;
|
|
922
|
+
// レイアウト再計算を強制(offsetHeightの読み取りでreflow発生)
|
|
923
|
+
document.body.offsetHeight;
|
|
924
|
+
// 次のペイントサイクルを待つ
|
|
925
|
+
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
|
|
926
|
+
});
|
|
927
|
+
// 開発用オーバーレイを非表示(ビューポートインジケーター等)
|
|
928
|
+
await page.evaluate(() => {
|
|
929
|
+
document.querySelectorAll('[data-debug], [data-viewport], .tailwind-indicator, .viewport-indicator').forEach(el => {
|
|
930
|
+
(el as HTMLElement).style.display = 'none';
|
|
931
|
+
});
|
|
932
|
+
// 右下の解像度表示バッジを非表示(absolute bottom-* right-* のパターン)
|
|
933
|
+
document.querySelectorAll('.absolute.bottom-2.right-2, .absolute.bottom-4.right-4').forEach(el => {
|
|
934
|
+
const text = el.textContent || '';
|
|
935
|
+
if (/\\d+.*×.*\\d+/.test(text)) {
|
|
936
|
+
(el as HTMLElement).style.display = 'none';
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
});
|
|
940
|
+
${errorCheckCode}
|
|
941
|
+
await page.screenshot({
|
|
942
|
+
path: '${screenshotPath}',
|
|
943
|
+
fullPage: true
|
|
944
|
+
});`;
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* アプリ別スクリーンショット撮影コードを生成
|
|
948
|
+
* baseUrlを考慮して絶対URLを構築
|
|
949
|
+
*/
|
|
950
|
+
function generateScreenshotCodeForApp(task, config, index, isFirstInGroup, baseUrl) {
|
|
951
|
+
const { screen, outputFileName } = task;
|
|
952
|
+
// 動的ルート解決(dynamicRoutes有効時はテンプレートリテラル形式)
|
|
953
|
+
const { route: resolvedRoute, isDynamic } = resolveRouteForDynamicMode(screen.route, config.routeParams, screen.name, config.screenOverrides, config.dynamicRoutes);
|
|
954
|
+
const screenshotPath = `${config.outputDir}/${outputFileName}`;
|
|
955
|
+
const waitFor = screen.waitFor || "networkidle";
|
|
956
|
+
const waitForSelectors = screen.waitForSelectors;
|
|
957
|
+
const delay = screen.delay ?? 500;
|
|
958
|
+
// セレクター待機コードを生成
|
|
959
|
+
const waitForSelectorsCode = waitForSelectors && waitForSelectors.length > 0
|
|
960
|
+
? waitForSelectors.map((selector) => ` await expect(page.locator('${selector.replace(/'/g, "\\'")}').first()).toBeVisible({ timeout: 10000 });`).join("\n") + "\n"
|
|
961
|
+
: "";
|
|
962
|
+
// ルート表示文字列(ログ用 - 動的な場合はプレースホルダーを含む)
|
|
963
|
+
const displayRoute = resolvedRoute.replace(/\$\{entityIds\.([a-zA-Z]+)\}/g, '{$1}');
|
|
964
|
+
// エラーチェックコードを生成
|
|
965
|
+
const errorCheckCode = generateErrorCheckCode(screen.name, displayRoute);
|
|
966
|
+
// baseUrlを適用(相対パスの場合のみ)
|
|
967
|
+
// 動的ルートの場合はテンプレートリテラルで構築
|
|
968
|
+
let gotoUrl;
|
|
969
|
+
if (isDynamic) {
|
|
970
|
+
// 動的ルート: テンプレートリテラル
|
|
971
|
+
if (resolvedRoute.startsWith('http://') || resolvedRoute.startsWith('https://')) {
|
|
972
|
+
// 既に絶対URL
|
|
973
|
+
gotoUrl = `\`${resolvedRoute}\``;
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
// 相対パス: baseUrlを先頭に追加
|
|
977
|
+
gotoUrl = `\`${baseUrl}${resolvedRoute}\``;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
// 静的ルート
|
|
982
|
+
if (resolvedRoute.startsWith('http://') || resolvedRoute.startsWith('https://')) {
|
|
983
|
+
// 既に絶対URL
|
|
984
|
+
gotoUrl = `'${resolvedRoute}'`;
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
// 相対パス: baseUrlを先頭に追加
|
|
988
|
+
gotoUrl = `'${baseUrl}${resolvedRoute}'`;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
// ダッシュボードルート(ロケールのみ)を判定
|
|
992
|
+
const dashboardRoutePattern = new RegExp(`^/${config.locale}/?$`);
|
|
993
|
+
const isDashboardRoute = !isDynamic && dashboardRoutePattern.test(resolvedRoute);
|
|
994
|
+
// ダッシュボードルートはログイン後すでにいるのでナビゲート不要(グループ内最初のスクリーンショット用)
|
|
995
|
+
if (isDashboardRoute && isFirstInGroup) {
|
|
996
|
+
return `
|
|
997
|
+
// === ${index + 1}. ${screen.name} ===
|
|
998
|
+
console.log(' [${index + 1}] ${screen.name} (${displayRoute})');
|
|
999
|
+
await page.waitForLoadState('load');
|
|
1000
|
+
${waitForSelectorsCode} await page.waitForTimeout(1000);
|
|
1001
|
+
// フォント読み込み完了とレイアウト安定化を待機
|
|
1002
|
+
await page.evaluate(async () => {
|
|
1003
|
+
await document.fonts.ready;
|
|
1004
|
+
// レイアウト再計算を強制(offsetHeightの読み取りでreflow発生)
|
|
1005
|
+
document.body.offsetHeight;
|
|
1006
|
+
// 次のペイントサイクルを待つ
|
|
1007
|
+
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
|
|
1008
|
+
});
|
|
1009
|
+
// 開発用オーバーレイを非表示(ビューポートインジケーター等)
|
|
1010
|
+
await page.evaluate(() => {
|
|
1011
|
+
document.querySelectorAll('[data-debug], [data-viewport], .tailwind-indicator, .viewport-indicator').forEach(el => {
|
|
1012
|
+
(el as HTMLElement).style.display = 'none';
|
|
1013
|
+
});
|
|
1014
|
+
// 右下の解像度表示バッジを非表示(absolute bottom-* right-* のパターン)
|
|
1015
|
+
document.querySelectorAll('.absolute.bottom-2.right-2, .absolute.bottom-4.right-4').forEach(el => {
|
|
1016
|
+
const text = el.textContent || '';
|
|
1017
|
+
if (/\\d+.*×.*\\d+/.test(text)) {
|
|
1018
|
+
(el as HTMLElement).style.display = 'none';
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
});
|
|
1022
|
+
${errorCheckCode}
|
|
1023
|
+
await page.screenshot({
|
|
1024
|
+
path: '${screenshotPath}',
|
|
1025
|
+
fullPage: true
|
|
1026
|
+
});`;
|
|
1027
|
+
}
|
|
1028
|
+
return `
|
|
1029
|
+
// === ${index + 1}. ${screen.name} ===
|
|
1030
|
+
console.log(' [${index + 1}] ${screen.name} (${isDynamic ? `' + ${gotoUrl} + '` : displayRoute})');
|
|
1031
|
+
await page.goto(${gotoUrl}, { waitUntil: 'domcontentloaded' });
|
|
1032
|
+
await page.waitForLoadState('${waitFor}', { timeout: 15000 }).catch(() => {
|
|
1033
|
+
console.log(' ⚠️ ${waitFor}タイムアウト - 続行');
|
|
1034
|
+
});
|
|
1035
|
+
${waitForSelectorsCode} await page.waitForTimeout(${delay});
|
|
1036
|
+
// フォント読み込み完了とレイアウト安定化を待機
|
|
1037
|
+
await page.evaluate(async () => {
|
|
1038
|
+
await document.fonts.ready;
|
|
1039
|
+
// レイアウト再計算を強制(offsetHeightの読み取りでreflow発生)
|
|
1040
|
+
document.body.offsetHeight;
|
|
1041
|
+
// 次のペイントサイクルを待つ
|
|
1042
|
+
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
|
|
1043
|
+
});
|
|
1044
|
+
// 開発用オーバーレイを非表示(ビューポートインジケーター等)
|
|
1045
|
+
await page.evaluate(() => {
|
|
1046
|
+
document.querySelectorAll('[data-debug], [data-viewport], .tailwind-indicator, .viewport-indicator').forEach(el => {
|
|
1047
|
+
(el as HTMLElement).style.display = 'none';
|
|
1048
|
+
});
|
|
1049
|
+
// 右下の解像度表示バッジを非表示(absolute bottom-* right-* のパターン)
|
|
1050
|
+
document.querySelectorAll('.absolute.bottom-2.right-2, .absolute.bottom-4.right-4').forEach(el => {
|
|
1051
|
+
const text = el.textContent || '';
|
|
1052
|
+
if (/\\d+.*×.*\\d+/.test(text)) {
|
|
1053
|
+
(el as HTMLElement).style.display = 'none';
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
});
|
|
1057
|
+
${errorCheckCode}
|
|
1058
|
+
await page.screenshot({
|
|
1059
|
+
path: '${screenshotPath}',
|
|
1060
|
+
fullPage: true
|
|
1061
|
+
});`;
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Playwright テストファイルを生成
|
|
1065
|
+
*
|
|
1066
|
+
* マルチアカウント対応:
|
|
1067
|
+
* - accounts 設定がある場合: アカウントごとにグループ化して撮影
|
|
1068
|
+
* - accounts 設定がない場合: 従来の単一アカウント方式(後方互換)
|
|
1069
|
+
*
|
|
1070
|
+
* 動的ルート解決:
|
|
1071
|
+
* - dynamicRoutes.enabled が true の場合: テストDBからエンティティIDを取得
|
|
1072
|
+
* - helperModule で指定されたモジュールから testDb をインポート
|
|
1073
|
+
*
|
|
1074
|
+
* ログイン回数を最小化するため、同一アカウントの画面をまとめて撮影
|
|
1075
|
+
*/
|
|
1076
|
+
function generateTestFile(screens, config, _projectPath, logger) {
|
|
1077
|
+
const tasks = expandScreensToTasks(screens, config, logger);
|
|
1078
|
+
const groupedTasks = groupTasksByAccount(tasks);
|
|
1079
|
+
const accounts = getEffectiveAccounts(config);
|
|
1080
|
+
const loginPath = getLoginPath(config);
|
|
1081
|
+
const multiAccountMode = isMultiAccountMode(config);
|
|
1082
|
+
const dynamicRoutesEnabled = config.dynamicRoutes.enabled;
|
|
1083
|
+
logger.info(`${tasks.length} 件のスクリーンショットタスクを生成 (${groupedTasks.size} アカウント)${dynamicRoutesEnabled ? " [動的ルート有効]" : ""}`);
|
|
1084
|
+
// アカウントグループごとのコード生成
|
|
1085
|
+
const accountGroups = [];
|
|
1086
|
+
let globalIndex = 0;
|
|
1087
|
+
for (const [accountName, accountTasks] of groupedTasks) {
|
|
1088
|
+
const accountConfig = accounts[accountName];
|
|
1089
|
+
const accountLabel = accountConfig.label || accountName;
|
|
1090
|
+
const isFirstAccount = accountGroups.length === 0;
|
|
1091
|
+
// ログイン/ログアウトコード
|
|
1092
|
+
const loginCode = isFirstAccount
|
|
1093
|
+
? `
|
|
1094
|
+
// ===== ${accountLabel} (${accountName}) でログイン =====
|
|
1095
|
+
console.log('\\n🔐 ${accountLabel} (${accountName}) でログイン中...');
|
|
1096
|
+
await page.goto('${loginPath}');
|
|
1097
|
+
await page.waitForLoadState('domcontentloaded');
|
|
1098
|
+
|
|
1099
|
+
await page.getByRole('textbox', { name: /Email|メールアドレス/i }).fill('${accountConfig.email}');
|
|
1100
|
+
await page.getByLabel(/^Password$|^パスワード$/i).fill('${accountConfig.password}');
|
|
1101
|
+
await page.getByRole('button', { name: /login|ログイン|Log in|Sign in/i }).click();
|
|
1102
|
+
|
|
1103
|
+
// ログイン成功を確認
|
|
1104
|
+
await page.waitForURL(/\\/(${config.locale})?(\\/|$)/, { timeout: 15000 });
|
|
1105
|
+
await expect(page.locator('[data-sidebar="sidebar"]').first()).toBeVisible({ timeout: 10000 });
|
|
1106
|
+
console.log('✅ ${accountLabel} でログイン成功');`
|
|
1107
|
+
: `
|
|
1108
|
+
// ===== ${accountLabel} (${accountName}) に切り替え =====
|
|
1109
|
+
console.log('\\n🔄 ${accountLabel} (${accountName}) に切り替え中...');
|
|
1110
|
+
|
|
1111
|
+
// ログアウト
|
|
1112
|
+
await page.goto('${loginPath}?logout=true');
|
|
1113
|
+
await page.waitForLoadState('domcontentloaded');
|
|
1114
|
+
// ログアウトボタンがある場合はクリック(サイト実装による)
|
|
1115
|
+
const logoutBtn = page.getByRole('button', { name: /logout|ログアウト|Sign out/i });
|
|
1116
|
+
if (await logoutBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
|
|
1117
|
+
await logoutBtn.click();
|
|
1118
|
+
await page.waitForLoadState('networkidle');
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// 再ログイン
|
|
1122
|
+
await page.goto('${loginPath}');
|
|
1123
|
+
await page.waitForLoadState('domcontentloaded');
|
|
1124
|
+
await page.getByRole('textbox', { name: /Email|メールアドレス/i }).fill('${accountConfig.email}');
|
|
1125
|
+
await page.getByLabel(/^Password$|^パスワード$/i).fill('${accountConfig.password}');
|
|
1126
|
+
await page.getByRole('button', { name: /login|ログイン|Log in|Sign in/i }).click();
|
|
1127
|
+
|
|
1128
|
+
// ログイン成功を確認
|
|
1129
|
+
await page.waitForURL(/\\/(${config.locale})?(\\/|$)/, { timeout: 15000 });
|
|
1130
|
+
await expect(page.locator('[data-sidebar="sidebar"]').first()).toBeVisible({ timeout: 10000 });
|
|
1131
|
+
console.log('✅ ${accountLabel} でログイン成功');`;
|
|
1132
|
+
// このアカウントで撮影する画面
|
|
1133
|
+
const screenshotSteps = accountTasks.map((task, localIndex) => {
|
|
1134
|
+
const isFirstInGroup = localIndex === 0;
|
|
1135
|
+
const code = generateScreenshotCode(task, config, globalIndex, isFirstInGroup);
|
|
1136
|
+
globalIndex++;
|
|
1137
|
+
return code;
|
|
1138
|
+
});
|
|
1139
|
+
accountGroups.push(`${loginCode}
|
|
1140
|
+
|
|
1141
|
+
// ${accountLabel} で ${accountTasks.length} 画面を撮影
|
|
1142
|
+
console.log('📷 ${accountLabel}: ${accountTasks.length}画面を撮影します...');
|
|
1143
|
+
${screenshotSteps.join("\n")}`);
|
|
1144
|
+
}
|
|
1145
|
+
// 統計情報
|
|
1146
|
+
const accountStats = Array.from(groupedTasks.entries())
|
|
1147
|
+
.map(([name, tasks]) => `${accounts[name]?.label || name}: ${tasks.length}画面`)
|
|
1148
|
+
.join(", ");
|
|
1149
|
+
// 動的ルート解決用のインポートとセットアップコード
|
|
1150
|
+
// helperModuleをtestFileの位置から相対パスに変換
|
|
1151
|
+
let helperImportPath = config.dynamicRoutes.helperModule ?? "./tests/helpers/database";
|
|
1152
|
+
if (dynamicRoutesEnabled && helperImportPath.startsWith("./")) {
|
|
1153
|
+
// testFileのディレクトリ深さを計算して相対パスを調整
|
|
1154
|
+
// 例: testFile = "scripts/screenshots/capture.ts" -> depth = 2
|
|
1155
|
+
// helperModule = "./tests/helpers/database" -> "../../tests/helpers/database"
|
|
1156
|
+
const testFileDir = config.testFile.split("/").slice(0, -1).join("/");
|
|
1157
|
+
const depth = testFileDir.split("/").filter(Boolean).length;
|
|
1158
|
+
const prefix = "../".repeat(depth);
|
|
1159
|
+
helperImportPath = prefix + helperImportPath.slice(2);
|
|
1160
|
+
}
|
|
1161
|
+
const dynamicRoutesImport = dynamicRoutesEnabled
|
|
1162
|
+
? `import { testDb } from '${helperImportPath}';\n`
|
|
1163
|
+
: "";
|
|
1164
|
+
const dynamicRoutesSetupCode = dynamicRoutesEnabled
|
|
1165
|
+
? `
|
|
1166
|
+
// ===== 動的ルート解決: テストDBからエンティティIDを取得 =====
|
|
1167
|
+
console.log('🔍 テストDBからエンティティIDを取得中...');
|
|
1168
|
+
await testDb.connect();
|
|
1169
|
+
const entityIds = await testDb.getScreenshotEntityIds();
|
|
1170
|
+
await testDb.disconnect();
|
|
1171
|
+
console.log('✅ エンティティID取得完了:', JSON.stringify(entityIds, null, 2));
|
|
1172
|
+
|
|
1173
|
+
// IDが取得できなかった場合は警告
|
|
1174
|
+
const missingIds = Object.entries(entityIds)
|
|
1175
|
+
.filter(([, value]) => value === null)
|
|
1176
|
+
.map(([key]) => key);
|
|
1177
|
+
if (missingIds.length > 0) {
|
|
1178
|
+
console.warn('⚠️ 以下のエンティティIDが取得できませんでした:', missingIds.join(', '));
|
|
1179
|
+
console.warn(' 該当する画面は404になる可能性があります');
|
|
1180
|
+
}
|
|
1181
|
+
`
|
|
1182
|
+
: "";
|
|
1183
|
+
const dynamicRoutesNote = dynamicRoutesEnabled
|
|
1184
|
+
? ` * 動的ルート: 有効(テストDBからエンティティIDを取得)\n *`
|
|
1185
|
+
: "";
|
|
1186
|
+
const testContent = `/**
|
|
1187
|
+
* Screen Screenshots for Documentation
|
|
1188
|
+
*
|
|
1189
|
+
* このファイルは shirokuma-docs screenshots コマンドで自動生成されました。
|
|
1190
|
+
* 手動編集は推奨されません。再生成時に上書きされます。
|
|
1191
|
+
*
|
|
1192
|
+
* 注意: これはE2Eテストではなく、ドキュメント用スクリーンショット撮影スクリプトです。
|
|
1193
|
+
* 実行: npx playwright test ${config.testFile}
|
|
1194
|
+
*
|
|
1195
|
+
* 生成日時: ${new Date().toISOString()}
|
|
1196
|
+
* 対象画面数: ${screens.length}
|
|
1197
|
+
* スクリーンショット数: ${tasks.length}
|
|
1198
|
+
* アカウント数: ${groupedTasks.size}${multiAccountMode ? ` (${accountStats})` : ""}
|
|
1199
|
+
* ソース: ${config.source}
|
|
1200
|
+
*${dynamicRoutesNote}
|
|
1201
|
+
* @generated
|
|
1202
|
+
*/
|
|
1203
|
+
|
|
1204
|
+
import { test, expect } from '@playwright/test';
|
|
1205
|
+
${dynamicRoutesImport}
|
|
1206
|
+
test.describe('Screen Screenshots for Documentation', () => {
|
|
1207
|
+
/**
|
|
1208
|
+
* 全画面のスクリーンショットを一括撮影
|
|
1209
|
+
*
|
|
1210
|
+
* ${multiAccountMode ? "マルチアカウントモード: アカウントごとにログインして撮影" : "シングルアカウントモード: 1回のログインで全画面撮影"}
|
|
1211
|
+
* - ログイン回数を最小化(アカウント数: ${groupedTasks.size})
|
|
1212
|
+
* - セッションが維持される(認証状態を共有)${dynamicRoutesEnabled ? "\n * - 動的ルート: テストDBからエンティティIDを取得" : ""}
|
|
1213
|
+
*/
|
|
1214
|
+
test('Capture all screenshots', async ({ page }) => {
|
|
1215
|
+
// タイムアウトを延長(全画面分 + アカウント切り替え分)
|
|
1216
|
+
test.setTimeout(${tasks.length * 30000 + groupedTasks.size * 30000 + 60000});
|
|
1217
|
+
|
|
1218
|
+
// ビューポート設定
|
|
1219
|
+
await page.setViewportSize({
|
|
1220
|
+
width: ${config.viewport.width},
|
|
1221
|
+
height: ${config.viewport.height}
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
// ビューポートサイズインジケーターが消えるのを待つ
|
|
1225
|
+
await page.waitForTimeout(2000);
|
|
1226
|
+
${dynamicRoutesSetupCode}${accountGroups.join("\n")}
|
|
1227
|
+
|
|
1228
|
+
console.log('\\n✅ 全${tasks.length}件のスクリーンショット撮影完了');
|
|
1229
|
+
});
|
|
1230
|
+
});
|
|
1231
|
+
`;
|
|
1232
|
+
return testContent;
|
|
1233
|
+
}
|
|
1234
|
+
//# sourceMappingURL=screenshots.js.map
|