diff --git a/client/src/templates/Challenges/redux/execute-challenge-saga.js b/client/src/templates/Challenges/redux/execute-challenge-saga.js index d2516f46e5d..6fabd023b5d 100644 --- a/client/src/templates/Challenges/redux/execute-challenge-saga.js +++ b/client/src/templates/Challenges/redux/execute-challenge-saga.js @@ -234,7 +234,8 @@ function* executeTests(testRunner, tests, testTimeout = 5000) { } // updates preview frame and the fcc console. -function* previewChallengeSaga({ flushLogs = true } = {}) { +export function* previewChallengeSaga(action) { + const flushLogs = action?.type !== actionTypes.previewMounted; yield delay(700); const isBuildEnabled = yield select(isBuildEnabledSelector); @@ -299,13 +300,16 @@ function* previewChallengeSaga({ flushLogs = true } = {}) { } } -function* updatePreviewSaga() { +// TODO: refactor this so that we can use a single saga for all challenge +// updates (then they can all go in the same `takeLatest` call and be cancelled +// appropriately) +function* updatePreviewSaga(action) { const challengeData = yield select(challengeDataSelector); if (challengeData.challengeType === challengeTypes.python) { yield updatePython(challengeData); } else { // all other challenges have to recreate the preview - yield previewChallengeSaga(); + yield previewChallengeSaga(action); } } @@ -348,12 +352,9 @@ export function createExecuteChallengeSaga(types) { takeLatest(types.executeChallenge, executeCancellableChallengeSaga), takeLatest(types.updateFile, updatePreviewSaga), takeLatest( - [types.challengeMounted, types.resetChallenge], + [types.challengeMounted, types.resetChallenge, types.previewMounted], previewChallengeSaga ), - takeLatest(types.previewMounted, previewChallengeSaga, { - flushLogs: false - }), takeLatest(types.projectPreviewMounted, previewProjectSolutionSaga) ]; } diff --git a/client/src/templates/Challenges/redux/execute-challenge-saga.test.js b/client/src/templates/Challenges/redux/execute-challenge-saga.test.js new file mode 100644 index 00000000000..dba368d8908 --- /dev/null +++ b/client/src/templates/Challenges/redux/execute-challenge-saga.test.js @@ -0,0 +1,44 @@ +import { expectSaga } from 'redux-saga-test-plan'; + +jest.mock('redux-saga/effects', () => ({ + ...jest.requireActual('redux-saga/effects'), + delay: jest.fn() +})); + +const initialState = { + challenge: { isBuildEnabled: true, isExecuting: false, challengeMeta: {} } +}; + +// We're not testing the reducer here, so just return the initial state +function reducer(state = initialState, _action) { + return state; +} + +import { previewChallengeSaga } from './execute-challenge-saga'; + +const challengeMounted = { type: 'challenge.challengeMounted' }; +const previewMounted = { type: 'challenge.previewMounted' }; +const resetChallenge = { type: 'challenge.resetChallenge' }; + +describe('execute-challenge-saga', () => { + it('flushes logs on challengeMounted', () => { + return expectSaga(previewChallengeSaga, challengeMounted) + .withReducer(reducer) + .put({ type: 'challenge.initLogs' }) + .silentRun(); + // TODO: figure out why silentRun is necessary. Without it, we get timeout + // warnings. Increasing the timeout just makes the tests take longer. + }); + it('flushes logs on reset', () => { + return expectSaga(previewChallengeSaga, resetChallenge) + .withReducer(reducer) + .put({ type: 'challenge.initLogs' }) + .silentRun(); + }); + it('does not flush logs on previewMounted', () => { + return expectSaga(previewChallengeSaga, previewMounted) + .withReducer(reducer) + .not.put({ type: 'challenge.initLogs' }) + .silentRun(); + }); +});