Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ Thank you for contributing to Storybook! Please submit all PRs to the `next` bra

#### Manual testing

_This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!_
> [!CAUTION]
> This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

<!-- Please include the steps to test your changes here. For example:

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/danger-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ on:
- labeled
- unlabeled
- edited
branches:
- main
- next

concurrency:
group: ${{ github.workflow }}-${{ github.event.number }}
Expand Down
1 change: 1 addition & 0 deletions frog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
frog
49 changes: 49 additions & 0 deletions scripts/dangerfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,56 @@ Bad examples:
}
};

/** @param {string} body */
const checkManualTestingSection = (body) => {
// Check if author is a core team member or maintainer
const author = danger.github.pr.user;
const authorAssociation = danger.github.pr.author_association;

// Bypass check for OWNER, MEMBER roles (but never for bots e.g. Copilot)
if (['OWNER', 'MEMBER'].includes(authorAssociation) && author.type !== 'Bot') {
// return; // TODO: re-enable after debug
}

// Check if manual testing section exists
const manualTestingMatch = body.match(/####\s*Manual testing/i);
if (!manualTestingMatch || manualTestingMatch.index === undefined) {
fail(
'PR description is missing the mandatory "#### Manual testing" section. Please add it so that reviewers know how to manually test your changes.'
);
return;
}

// Extract content after the manual testing section
const manualTestingSectionStart = manualTestingMatch.index + manualTestingMatch[0].length;
const restOfBody = body.substring(manualTestingSectionStart);

// Find the next section
const nextSectionMatch = restOfBody.match(/\n#+[^#]/);
const manualTestingContent = nextSectionMatch
? restOfBody.substring(0, nextSectionMatch.index)
: restOfBody;

// Remove the caution block and check if there's any meaningful content left
const contentWithoutCaution = manualTestingContent
.replace(/>\s*\[!CAUTION\][^]*?This section is mandatory[^]*?Thanks!/i, '')
.trim();

// Check if there's any substantial content (ignoring whitespace and template comments)
const hasContent =
contentWithoutCaution
.replace(/<!--[^]*?-->/g, '') // Remove HTML comments
.replace(/\s+/g, '').length > 0; // Remove all whitespace

if (!hasContent) {
fail(
'The "#### Manual testing" section is mandatory. Please describe how to test the changes you\'ve made, step by step, so that reviewers can confirm your PR works as intended.'
);
}
};

if (prLogConfig) {
checkRequiredLabels(labels.map((l) => l.name));
checkPrTitle(danger.github.pr.title);
checkManualTestingSection(danger.github.pr.body);
}