Skip to content

Commit 4174001

Browse files
feat(debrief): make debrief page customizable via batch config (#1214)
* feat(debrief): make debrief page customizable via batch config (#1008) Add a required `debrief` field to batch config that accepts a path to a markdown file or "none". Custom markdown is fetched from CDN and rendered on the final page; "none" shows a generic thank-you message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(debrief): address Copilot review feedback - Use `=== undefined` instead of falsy check for loading guard - Handle `useText` error state (fall back to generic message) - Show inline loading for debrief section instead of full-page Loading - Use nullish coalescing (`??`) so empty markdown files render correctly - Replace `waitForFunction(() => true)` with `expect.poll()` in tests - Update DEBRIEF-007 test to verify exit code visible during debrief load Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5eb86f4 commit 4174001

27 files changed

Lines changed: 323 additions & 15 deletions

client/src/intro-exit/Debrief.jsx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
11
/*
22
Debrief page:
3-
States research purpose, includes CSSLab contact information
3+
Shows custom or generic debrief content, exit code, and close prompt.
44
*/
55

66
import React, { useEffect } from "react";
77
import { usePlayer } from "@empirica/core/player/classic/react";
8-
import { Loading } from "@empirica/core/player/react";
8+
import { useGlobal, Loading } from "@empirica/core/player/react";
99
import { Markdown } from "../components/Markdown";
10+
import { useText } from "../components/hooks";
1011
import { useIdleContext } from "../components/IdleProvider";
1112
import { Button } from "../components/Button";
1213

1314
export function Debrief() {
1415
const player = usePlayer();
16+
const globals = useGlobal();
1517
const { setAllowIdle } = useIdleContext();
1618

19+
const batchConfig = globals?.get("recruitingBatchConfig");
20+
const debriefPath =
21+
batchConfig?.debrief && batchConfig.debrief !== "none"
22+
? batchConfig.debrief
23+
: null;
24+
const { text: debriefText, error: debriefError } = useText({
25+
file: debriefPath,
26+
});
27+
1728
useEffect(() => {
18-
// Set allowIdle to true when the component loads
1929
setAllowIdle(true);
2030
console.log("Set Allow Idle");
2131

22-
// Reset allowIdle to false when the component unloads
2332
return () => {
2433
setAllowIdle(false);
2534
console.log("Clear Allow Idle");
@@ -31,6 +40,11 @@ export function Debrief() {
3140
}
3241

3342
const exitCodes = player.get("exitCodes");
43+
const debriefLoading =
44+
debriefPath && debriefText === undefined && !debriefError;
45+
const debriefContent = debriefPath
46+
? debriefText ?? "Thank you for participating."
47+
: "Thank you for participating.";
3448

3549
const copyToClipboard = () => {
3650
navigator.clipboard.writeText(exitCodes.complete);
@@ -40,16 +54,6 @@ export function Debrief() {
4054
);
4155
};
4256

43-
const debriefStatements = `
44-
### About this study
45-
_Social scientists have tested different ways to improve small group conversations, like using a facilitator, setting an agenda, or creating ground rules. While some methods work better than others, it’s challenging to know which will be the most effective. What we do know is that different types of conversations need different kinds of support._
46-
47-
_Instead of looking for one solution that works for all conversations, our research team is mapping out what helps each specific type of conversation. Your participation in this study provides valuable data that, combined with data from other discussions, will help us understand how the context of a discussion shapes its outcomes._
48-
49-
_For any additional questions, please contact the University of Pennsylvania research team by
50-
emailing **[deliberation-study@wharton.upenn.edu](mailto:deliberation-study@wharton.upenn.edu)**._
51-
`;
52-
5357
return (
5458
<div className="grid justify-center">
5559
<h1>
@@ -78,7 +82,7 @@ emailing **[deliberation-study@wharton.upenn.edu](mailto:deliberation-study@whar
7882
</div>
7983
)}
8084

81-
<Markdown text={debriefStatements} />
85+
{debriefLoading ? <Loading /> : <Markdown text={debriefContent} />}
8286

8387
<h3>You may now close this window.</h3>
8488
</div>

cypress/e2e/00_Naked_URL.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const configJson = `{
1313
"customIdInstructions": "none",
1414
"platformConsent": "US",
1515
"consentAddendum": "none",
16+
"debrief": "none",
1617
"checkAudio": false,
1718
"checkVideo": false,
1819
"introSequence": "none",

cypress/e2e/01_Normal_Paths_Omnibus.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ describe(
1717
"customIdInstructions": {"MyId":"projects/example/customIdInstructions.md", "default":"projects/example/defaultIdInstructions.md"},
1818
"platformConsent": "US",
1919
"consentAddendum": "projects/example/consentAddendum.md",
20+
"debrief": "projects/example/testDebrief.md",
2021
"checkAudio": true,
2122
"checkVideo": true,
2223
"introSequence": "cypress_intro",
@@ -940,6 +941,19 @@ describe(
940941
}
941942
);
942943

944+
// Verify custom debrief content from testDebrief.md is rendered
945+
cy.get(`[data-player-id="${playerKeys[0]}"]`).contains(
946+
"custom debrief message for testing purposes"
947+
);
948+
cy.get(`[data-player-id="${playerKeys[0]}"]`).contains(
949+
"You may now close this window."
950+
);
951+
// Verify old hardcoded debrief text is NOT present
952+
cy.get(`[data-player-id="${playerKeys[0]}"]`).should(
953+
"not.contain",
954+
"Social scientists have tested different ways"
955+
);
956+
943957
// wait for data to be saved (should be fast)
944958
cy.wait(3000);
945959

cypress/e2e/02_Batch_Canceled.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const configJsonA = `{
77
"customIdInstructions": "none",
88
"platformConsent": "US",
99
"consentAddendum": "none",
10+
"debrief": "none",
1011
"checkVideo": true,
1112
"checkAudio": true,
1213
"introSequence": "none",
@@ -46,6 +47,7 @@ const configJsonB = `{
4647
"customIdInstructions": {"MyId":"projects/example/customIdInstructions.md", "default":"projects/example/defaultIdInstructions.md"},
4748
"platformConsent": "US",
4849
"consentAddendum": "none",
50+
"debrief": "none",
4951
"checkVideo": true,
5052
"checkAudio": true,
5153
"treatments": [

cypress/e2e/03_Text_Chat.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ describe(
1313
"customIdInstructions": "none",
1414
"platformConsent": "US",
1515
"consentAddendum": "none",
16+
"debrief": "none",
1617
"checkAudio": false,
1718
"checkVideo": false,
1819
"introSequence": "none",

cypress/e2e/05_Mobile_Check.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const configJson = `{
99
"customIdInstructions": "none",
1010
"platformConsent": "US",
1111
"consentAddendum": "none",
12+
"debrief": "none",
1213
"checkAudio": false,
1314
"checkVideo": false,
1415
"introSequence": "none",

cypress/e2e/06_Many_Games.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const configJson = `{
55
"customIdInstructions": "none",
66
"platformConsent": "UK",
77
"consentAddendum": "none",
8+
"debrief": "none",
89
"checkAudio": true,
910
"checkVideo": true,
1011
"introSequence": "none",

cypress/e2e/07_Returning_Player.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ describe("Returning Player", { retries: { runMode: 2, openMode: 0 } }, () => {
1010
"customIdInstructions": "none",
1111
"platformConsent": "US",
1212
"consentAddendum": "none",
13+
"debrief": "none",
1314
"checkAudio": false,
1415
"checkVideo": false,
1516
"introSequence": "none",

cypress/e2e/08_Invalid_configs.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ describe("Returning Player", { retries: { runMode: 2, openMode: 0 } }, () => {
1111
"customIdInstructions": "none",
1212
"platformConsent": "US",
1313
"consentAddendum": "none",
14+
"debrief": "none",
1415
"checkAudio": true,
1516
"checkVideo": true,
1617
"introSequence": "none",
@@ -99,6 +100,7 @@ describe("Returning Player", { retries: { runMode: 2, openMode: 0 } }, () => {
99100
"customIdInstructions": "none",
100101
"platformConsent": "US",
101102
"consentAddendum": "none",
103+
"debrief": "none",
102104
"checkAudio": false,
103105
"checkVideo": false,
104106
"introSequence": "none",

cypress/e2e/09_Many_Players.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const configJson = `{
55
"customIdInstructions": "none",
66
"platformConsent": "US",
77
"consentAddendum": "none",
8+
"debrief": "none",
89
"checkAudio": false,
910
"checkVideo": false,
1011
"introSequence": "none",

0 commit comments

Comments
 (0)