From 96d62330cd74bc1414d6c2eb34cc4925329d37d3 Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Fri, 28 Feb 2025 13:03:18 +0100 Subject: [PATCH] feat(client): support beforeAll in DOM challenge tests (#59001) --- client/src/redux/prop-types.ts | 1 + .../src/templates/Challenges/classic/show.tsx | 9 +++ .../Challenges/redux/action-types.js | 1 + .../src/templates/Challenges/redux/actions.js | 1 + .../redux/execute-challenge-saga.js | 4 +- .../src/templates/Challenges/redux/index.js | 4 ++ .../templates/Challenges/redux/selectors.js | 1 + .../src/templates/Challenges/utils/frame.ts | 49 +++++++++---- .../641da803d9892447d059804e.md | 6 ++ .../641da836581c254815f785fe.md | 5 ++ .../641da86294fd9f485d3c2bf0.md | 6 ++ .../641daa8c2c3e364ac3650b37.md | 6 ++ .../641da42481d90c4314c99e94.md | 6 ++ .../641da465273051435d332b15.md | 6 ++ .../641da4b16937be43ba24c63d.md | 5 ++ .../641da51a9810e74411262fcc.md | 6 ++ .../641da5462576784453146ec2.md | 6 ++ .../641da5abaac81844a54adb03.md | 6 ++ .../641da73b09e7f046c758e0ed.md | 6 ++ .../641da791d0c34a472b8d15b6.md | 6 ++ .../641da803d9892447d059804e.md | 10 ++- .../641da836581c254815f785fe.md | 5 ++ .../641da86294fd9f485d3c2bf0.md | 6 ++ .../641da895fb7ec648a5bdf19c.md | 6 ++ .../641da8db2a036048ebe6999e.md | 6 ++ .../641da97c987a514959ada414.md | 6 ++ .../641da9aceb788e49a73ebcc9.md | 6 ++ .../641da9ea9b847a49fe6ee9b6.md | 6 ++ .../641daa5ea050f24a7cade6e6.md | 6 ++ .../641daa8c2c3e364ac3650b37.md | 6 ++ .../641daae5e18eae4b562633e4.md | 6 ++ .../641dab13c1b6f14b9828e6b1.md | 6 ++ curriculum/schema/challenge-schema.js | 3 + curriculum/test/test-challenges.js | 33 ++++++--- .../with-another-invalid-before-hook.md | 34 +++++++++ .../parser/__fixtures__/with-before-hook.md | 40 +++++++++++ .../__fixtures__/with-empty-before-hook.md | 32 +++++++++ .../__fixtures__/with-invalid-before-hook.md | 42 +++++++++++ .../__fixtures__/with-non-js-before-hook.md | 40 +++++++++++ tools/challenge-parser/parser/index.js | 2 + .../add-before-hook.test.js.snap | 13 ++++ .../parser/plugins/add-before-hook.js | 33 +++++++++ .../parser/plugins/add-before-hook.test.js | 69 +++++++++++++++++++ 43 files changed, 531 insertions(+), 25 deletions(-) create mode 100644 tools/challenge-parser/parser/__fixtures__/with-another-invalid-before-hook.md create mode 100644 tools/challenge-parser/parser/__fixtures__/with-before-hook.md create mode 100644 tools/challenge-parser/parser/__fixtures__/with-empty-before-hook.md create mode 100644 tools/challenge-parser/parser/__fixtures__/with-invalid-before-hook.md create mode 100644 tools/challenge-parser/parser/__fixtures__/with-non-js-before-hook.md create mode 100644 tools/challenge-parser/parser/plugins/__snapshots__/add-before-hook.test.js.snap create mode 100644 tools/challenge-parser/parser/plugins/add-before-hook.js create mode 100644 tools/challenge-parser/parser/plugins/add-before-hook.test.js diff --git a/client/src/redux/prop-types.ts b/client/src/redux/prop-types.ts index be0db623874..e349ceef89a 100644 --- a/client/src/redux/prop-types.ts +++ b/client/src/redux/prop-types.ts @@ -172,6 +172,7 @@ export type ChallengeNode = { head: string[]; hasEditableBoundaries: boolean; helpCategory: string; + hooks?: { beforeAll: string }; id: string; instructions: string; isComingSoon: boolean; diff --git a/client/src/templates/Challenges/classic/show.tsx b/client/src/templates/Challenges/classic/show.tsx index 5c71f40b7aa..c8baf9a856d 100644 --- a/client/src/templates/Challenges/classic/show.tsx +++ b/client/src/templates/Challenges/classic/show.tsx @@ -45,6 +45,7 @@ import { executeChallenge, initConsole, initTests, + initHooks, initVisibleEditors, previewMounted, updateChallengeMeta, @@ -86,6 +87,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => createFiles, initConsole, initTests, + initHooks, initVisibleEditors, updateChallengeMeta, challengeMounted, @@ -108,6 +110,7 @@ interface ShowClassicProps extends Pick { challengeFiles: ChallengeFiles; initConsole: (arg0: string) => void; initTests: (tests: Test[]) => void; + initHooks: (hooks?: { beforeAll: string }) => void; initVisibleEditors: () => void; isChallengeCompleted: boolean; output: string[]; @@ -193,6 +196,7 @@ function ShowClassic({ title, description, instructions, + hooks, fields: { tests, blockName }, challengeType, hasEditableBoundaries, @@ -216,6 +220,7 @@ function ShowClassic({ challengeMounted, initConsole, initTests, + initHooks, initVisibleEditors, updateChallengeMeta, openModal, @@ -359,6 +364,7 @@ function ShowClassic({ ); initTests(tests); + initHooks(hooks); initVisibleEditors(); @@ -556,6 +562,9 @@ export const query = graphql` superBlock translationPending forumTopicId + hooks { + beforeAll + } fields { blockName slug diff --git a/client/src/templates/Challenges/redux/action-types.js b/client/src/templates/Challenges/redux/action-types.js index d9d2efd3fea..a988ea8bdee 100644 --- a/client/src/templates/Challenges/redux/action-types.js +++ b/client/src/templates/Challenges/redux/action-types.js @@ -8,6 +8,7 @@ export const actionTypes = createTypes( [ 'createFiles', 'createQuestion', + 'initHooks', 'initTests', 'initConsole', 'initLogs', diff --git a/client/src/templates/Challenges/redux/actions.js b/client/src/templates/Challenges/redux/actions.js index 7ccb65f257f..39970babac0 100644 --- a/client/src/templates/Challenges/redux/actions.js +++ b/client/src/templates/Challenges/redux/actions.js @@ -22,6 +22,7 @@ export const createFiles = createAction( export const createQuestion = createAction(actionTypes.createQuestion); export const initTests = createAction(actionTypes.initTests); +export const initHooks = createAction(actionTypes.initHooks); export const updateTests = createAction(actionTypes.updateTests); export const cancelTests = createAction(actionTypes.cancelTests); export const initConsole = createAction(actionTypes.initConsole); diff --git a/client/src/templates/Challenges/redux/execute-challenge-saga.js b/client/src/templates/Challenges/redux/execute-challenge-saga.js index 9514a4a3290..4e51f880c41 100644 --- a/client/src/templates/Challenges/redux/execute-challenge-saga.js +++ b/client/src/templates/Challenges/redux/execute-challenge-saga.js @@ -55,6 +55,7 @@ import { challengeDataSelector, challengeMetaSelector, challengeTestsSelector, + challengeHooksSelector, isBuildEnabledSelector, isExecutingSelector, portalDocumentSelector, @@ -110,6 +111,7 @@ export function* executeChallengeSaga({ payload }) { const tests = (yield select(challengeTestsSelector)).map( ({ text, testString }) => ({ text, testString }) ); + const hooks = yield select(challengeHooksSelector); yield put(updateTests(tests)); yield fork(takeEveryLog, consoleProxy); @@ -128,7 +130,7 @@ export function* executeChallengeSaga({ payload }) { const document = yield getContext('document'); const testRunner = yield call( getTestRunner, - buildData, + { ...buildData, hooks }, { proxyLogger }, document ); diff --git a/client/src/templates/Challenges/redux/index.js b/client/src/templates/Challenges/redux/index.js index a24ffab0d3a..b24916a75bb 100644 --- a/client/src/templates/Challenges/redux/index.js +++ b/client/src/templates/Challenges/redux/index.js @@ -120,6 +120,10 @@ export const reducer = handleActions( ...state, challengeTests: payload }), + [actionTypes.initHooks]: (state, { payload }) => ({ + ...state, + challengeHooks: payload + }), [actionTypes.updateTests]: (state, { payload }) => ({ ...state, challengeTests: payload diff --git a/client/src/templates/Challenges/redux/selectors.js b/client/src/templates/Challenges/redux/selectors.js index 437630d1fcb..0c3d82972ca 100644 --- a/client/src/templates/Challenges/redux/selectors.js +++ b/client/src/templates/Challenges/redux/selectors.js @@ -14,6 +14,7 @@ import { ns } from './action-types'; export const challengeFilesSelector = state => state[ns].challengeFiles; export const challengeMetaSelector = state => state[ns].challengeMeta; +export const challengeHooksSelector = state => state[ns].challengeHooks; export const challengeTestsSelector = state => state[ns].challengeTests; export const consoleOutputSelector = state => state[ns].consoleOut; export const completedChallengesIdsSelector = createSelector( diff --git a/client/src/templates/Challenges/utils/frame.ts b/client/src/templates/Challenges/utils/frame.ts index 6c8d4d0eb54..090841425ee 100644 --- a/client/src/templates/Challenges/utils/frame.ts +++ b/client/src/templates/Challenges/utils/frame.ts @@ -16,6 +16,10 @@ export interface Source { original: { [key: string]: string | null }; } +interface Hooks { + beforeAll?: string; +} + export interface Context { window?: Window & typeof globalThis & { i18nContent?: i18n; __pyodide: unknown }; @@ -23,6 +27,7 @@ export interface Context { element: HTMLIFrameElement; build: string; sources: Source; + hooks?: Hooks; loadEnzyme?: () => void; } @@ -89,7 +94,7 @@ const DOCUMENT_NOT_FOUND_ERROR = 'misc.document-notfound'; // The "fcc-hide-header" class on line 95 is added to ensure that the CSSHelper class ignores this style element // during tests, preventing CSS-related test failures. -export const createHeader = (id = mainPreviewId) => +const createHeader = (id = mainPreviewId) => `