Skip to content

Commit

Permalink
Add Full TypeScript chapter (source-academy#2379)
Browse files Browse the repository at this point in the history
* Add full TS chapter

* Suppress ts-morph warnings

* Add tests

* Bump js-slang

* Fix workaround

* Increase cache size

* Remove tabs for full TS
  • Loading branch information
zhaojj2209 authored and RichDom2185 committed Jul 15, 2023
1 parent 1704770 commit cec8f52
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 75 deletions.
5 changes: 4 additions & 1 deletion craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const cracoConfig = (module.exports = {
plugin => plugin.constructor.name === 'InjectManifest'
);
if (injectManifestPlugin) {
injectManifestPlugin.config.maximumFileSizeToCacheInBytes = 10 * 1024 * 1024;
injectManifestPlugin.config.maximumFileSizeToCacheInBytes = 15 * 1024 * 1024;
}

// add rules to pack WASM (for Sourceror)
Expand Down Expand Up @@ -77,6 +77,9 @@ const cracoConfig = (module.exports = {
})
];

// Workaround to suppress warnings caused by ts-morph in js-slang
webpackConfig.module.noParse = /typescript\.js$/;

return webpackConfig;
}
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"classnames": "^2.3.2",
"flexboxgrid": "^6.3.1",
"flexboxgrid-helpers": "^1.1.3",
"js-slang": "^1.0.15",
"js-slang": "^1.0.16",
"konva": "^8.3.14",
"lodash": "^4.17.21",
"lz-string": "^1.4.4",
Expand Down
8 changes: 8 additions & 0 deletions src/commons/application/ApplicationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ export const fullJSLanguage: SALanguage = {
displayName: 'full JavaScript'
};

export const fullTSLanguage: SALanguage = {
chapter: Chapter.FULL_TS,
variant: Variant.DEFAULT,
displayName: 'full TypeScript'
};

export const htmlLanguage: SALanguage = {
chapter: Chapter.HTML,
variant: Variant.DEFAULT,
Expand All @@ -138,6 +144,8 @@ export const styliseSublanguage = (chapter: Chapter, variant: Variant = Variant.
switch (chapter) {
case Chapter.FULL_JS:
return fullJSLanguage.displayName;
case Chapter.FULL_TS:
return fullTSLanguage.displayName;
case Chapter.HTML:
return htmlLanguage.displayName;
default:
Expand Down
3 changes: 3 additions & 0 deletions src/commons/controlBar/ControlBarChapterSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React from 'react';
import {
defaultLanguages,
fullJSLanguage,
fullTSLanguage,
htmlLanguage,
SALanguage,
sourceLanguages,
Expand All @@ -31,11 +32,13 @@ const chapterListRenderer: ItemListRenderer<SALanguage> = ({ itemsParentRef, ren
const defaultChoices = defaultLanguages.map(renderItem);
const variantChoices = variantLanguages.map(renderItem);
const fullJSChoice = renderItem(fullJSLanguage, 0);
const fullTSChoice = renderItem(fullTSLanguage, 0);
const htmlChoice = renderItem(htmlLanguage, 0);
return (
<Menu ulRef={itemsParentRef}>
{defaultChoices}
{Constants.playgroundOnly && fullJSChoice}
{Constants.playgroundOnly && fullTSChoice}
{Constants.playgroundOnly && htmlChoice}
<MenuItem key="variant-menu" text="Variants" icon="cog">
{variantChoices}
Expand Down
33 changes: 0 additions & 33 deletions src/commons/fullJS/FullJSUtils.tsx

This file was deleted.

25 changes: 0 additions & 25 deletions src/commons/html/HTMLUtils.tsx

This file was deleted.

4 changes: 3 additions & 1 deletion src/commons/sagas/WorkspaceSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ import {
} from '../application/types/InterpreterTypes';
import { Library, Testcase, TestcaseType, TestcaseTypes } from '../assessment/AssessmentTypes';
import { Documentation } from '../documentation/Documentation';
import { showFullJSDisclaimer } from '../fullJS/FullJSUtils';
import { SideContentType } from '../sideContent/SideContentTypes';
import { actions } from '../utils/ActionsHelper';
import DisplayBufferService from '../utils/DisplayBufferService';
Expand All @@ -58,6 +57,7 @@ import {
} from '../utils/JsSlangHelper';
import { showSuccessMessage, showWarningMessage } from '../utils/NotificationsHelper';
import { makeExternalBuiltins as makeSourcerorExternalBuiltins } from '../utils/SourcerorHelper';
import { showFullJSDisclaimer, showFullTSDisclaimer } from '../utils/WarningDialogHelper';
import { notifyProgramEvaluated } from '../workspace/WorkspaceActions';
import {
ADD_HTML_CONSOLE_ERROR,
Expand Down Expand Up @@ -309,6 +309,8 @@ export default function* WorkspaceSaga(): SagaIterator {
const toChangeChapter: boolean =
newChapter === Chapter.FULL_JS
? chapterChanged && (yield call(showFullJSDisclaimer))
: newChapter === Chapter.FULL_TS
? chapterChanged && (yield call(showFullTSDisclaimer))
: chapterChanged;

if (toChangeChapter) {
Expand Down
70 changes: 64 additions & 6 deletions src/commons/sagas/__tests__/WorkspaceSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Chapter, Finished, Variant } from 'js-slang/dist/types';
import { call } from 'redux-saga/effects';
import { expectSaga } from 'redux-saga-test-plan';
import * as matchers from 'redux-saga-test-plan/matchers';
import * as fullJSUtils from 'src/commons/fullJS/FullJSUtils';
import { showFullJSDisclaimer, showFullTSDisclaimer } from 'src/commons/utils/WarningDialogHelper';

import {
beginInterruptExecution,
Expand All @@ -16,7 +16,12 @@ import {
evalTestcaseFailure,
evalTestcaseSuccess
} from '../../application/actions/InterpreterActions';
import { defaultState, fullJSLanguage, OverallState } from '../../application/ApplicationTypes';
import {
defaultState,
fullJSLanguage,
fullTSLanguage,
OverallState
} from '../../application/ApplicationTypes';
import { externalLibraries, ExternalLibraryName } from '../../application/types/ExternalTypes';
import {
BEGIN_DEBUG_PAUSE,
Expand Down Expand Up @@ -539,9 +544,9 @@ describe('CHAPTER_SELECT', () => {
};

return expectSaga(workspaceSaga)
.provide([[matchers.call.fn(fullJSUtils.showFullJSDisclaimer), true]])
.provide([[matchers.call.fn(showFullJSDisclaimer), true]])
.withState(newDefaultState)
.call(fullJSUtils.showFullJSDisclaimer)
.call(showFullJSDisclaimer)
.put(beginClearContext(workspaceLocation, library, false))
.put(clearReplOutput(workspaceLocation))
.call(showSuccessMessage, `Switched to full JavaScript`, 1000)
Expand All @@ -560,9 +565,9 @@ describe('CHAPTER_SELECT', () => {
const newDefaultState = generateDefaultState(workspaceLocation, { context, globals });

return expectSaga(workspaceSaga)
.provide([[matchers.call.fn(fullJSUtils.showFullJSDisclaimer), false]])
.provide([[matchers.call.fn(showFullJSDisclaimer), false]])
.withState(newDefaultState)
.call(fullJSUtils.showFullJSDisclaimer)
.call(showFullJSDisclaimer)
.not.put.actionType(BEGIN_CLEAR_CONTEXT)
.not.put.actionType(CLEAR_REPL_OUTPUT)
.not.call.fn(showSuccessMessage)
Expand All @@ -577,6 +582,59 @@ describe('CHAPTER_SELECT', () => {
.silentRun();
});
});

describe('show disclaimer when fullTS is chosen', () => {
test('correct actions when user proceeds', () => {
const newDefaultState = generateDefaultState(workspaceLocation, { context, globals });
const library: Library = {
chapter: fullTSLanguage.chapter,
variant: fullTSLanguage.variant,
external: {
name: 'NONE' as ExternalLibraryName,
symbols: context.externalSymbols
},
globals
};

return expectSaga(workspaceSaga)
.provide([[matchers.call.fn(showFullTSDisclaimer), true]])
.withState(newDefaultState)
.call(showFullTSDisclaimer)
.put(beginClearContext(workspaceLocation, library, false))
.put(clearReplOutput(workspaceLocation))
.call(showSuccessMessage, `Switched to full TypeScript`, 1000)
.dispatch({
type: CHAPTER_SELECT,
payload: {
chapter: fullTSLanguage.chapter,
variant: fullTSLanguage.variant,
workspaceLocation
}
})
.silentRun();
});

test('correct actions when user cancels', () => {
const newDefaultState = generateDefaultState(workspaceLocation, { context, globals });

return expectSaga(workspaceSaga)
.provide([[matchers.call.fn(showFullTSDisclaimer), false]])
.withState(newDefaultState)
.call(showFullTSDisclaimer)
.not.put.actionType(BEGIN_CLEAR_CONTEXT)
.not.put.actionType(CLEAR_REPL_OUTPUT)
.not.call.fn(showSuccessMessage)
.dispatch({
type: CHAPTER_SELECT,
payload: {
chapter: fullTSLanguage.chapter,
variant: fullTSLanguage.variant,
workspaceLocation
}
})
.silentRun();
});
});
});

describe('PLAYGROUND_EXTERNAL_SELECT', () => {
Expand Down
91 changes: 91 additions & 0 deletions src/commons/utils/WarningDialogHelper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { showSimpleConfirmDialog, SimpleConfirmDialogProps } from './DialogHelper';
import { showWarningMessage } from './NotificationsHelper';

// Full JS
const FULL_JS_DISCLAIMER_DIALOG_PROPS: SimpleConfirmDialogProps = {
icon: 'warning-sign',
title: 'You are switching to a unsafe feature!',
contents: (
<p>
<code>full JavaScript</code> allows you to run arbitrary JS code beyond source.
<br />
<br />
This might pose a security concern if you are not careful and fully aware of what program you
are running!
<br />
<br />
<strong>Do you still want to proceed?</strong>
</p>
),
positiveIntent: 'danger',
positiveLabel: 'Proceed',
negativeLabel: 'Cancel'
};

const FULL_JS_URL_LOAD_INFO: string =
'For security concerns, users are not allowed to load full JavaScript code from shared links';

export function showFullJSDisclaimer(): Promise<boolean> {
return showSimpleConfirmDialog(FULL_JS_DISCLAIMER_DIALOG_PROPS);
}

export function showFullJSWarningOnUrlLoad(): void {
showWarningMessage(FULL_JS_URL_LOAD_INFO);
}

// Full TS
const FULL_TS_DISCLAIMER_DIALOG_PROPS: SimpleConfirmDialogProps = {
icon: 'warning-sign',
title: 'You are switching to a unsafe feature!',
contents: (
<p>
<code>full TypeScript</code> allows you to run arbitrary JS code beyond source.
<br />
<br />
This might pose a security concern if you are not careful and fully aware of what program you
are running!
<br />
<br />
<strong>Do you still want to proceed?</strong>
</p>
),
positiveIntent: 'danger',
positiveLabel: 'Proceed',
negativeLabel: 'Cancel'
};

const FULL_TS_URL_LOAD_INFO: string =
'For security concerns, users are not allowed to load full JavaScript code from shared links';

export function showFullTSDisclaimer(): Promise<boolean> {
return showSimpleConfirmDialog(FULL_TS_DISCLAIMER_DIALOG_PROPS);
}

export function showFulTSWarningOnUrlLoad(): void {
showWarningMessage(FULL_TS_URL_LOAD_INFO);
}

// HTML
const HTML_DISCLAIMER_DIALOG_PROPS: SimpleConfirmDialogProps = {
icon: 'warning-sign',
title: 'Beware when running shared HTML code!',
contents: (
<p>
You are about to access HTML code written by others, which may contain malicious scripts.
<br />
<br />
This might pose a security concern if you are not careful and fully aware of what the shared
HTML code contains!
<br />
<br />
<strong>Do you still want to proceed?</strong>
</p>
),
positiveIntent: 'danger',
positiveLabel: 'Proceed',
negativeLabel: 'Cancel'
};

export function showHTMLDisclaimer(): Promise<boolean> {
return showSimpleConfirmDialog(HTML_DISCLAIMER_DIALOG_PROPS);
}
14 changes: 11 additions & 3 deletions src/pages/playground/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ import {
setEditorSessionId,
setSharedbConnected
} from 'src/commons/collabEditing/CollabEditingActions';
import { showFullJSWarningOnUrlLoad } from 'src/commons/fullJS/FullJSUtils';
import { showHTMLDisclaimer } from 'src/commons/html/HTMLUtils';
import SideContentHtmlDisplay from 'src/commons/sideContent/SideContentHtmlDisplay';
import { useResponsive, useTypedSelector } from 'src/commons/utils/Hooks';
import {
showFullJSWarningOnUrlLoad,
showFulTSWarningOnUrlLoad,
showHTMLDisclaimer
} from 'src/commons/utils/WarningDialogHelper';
import {
addHtmlConsoleError,
browseReplHistoryDown,
Expand Down Expand Up @@ -173,6 +176,8 @@ export async function handleHash(hash: string, props: PlaygroundProps) {
const chapter = stringParamToInt(qs.chap) || undefined;
if (chapter === Chapter.FULL_JS) {
showFullJSWarningOnUrlLoad();
} else if (chapter === Chapter.FULL_TS) {
showFulTSWarningOnUrlLoad();
} else {
if (chapter === Chapter.HTML) {
const continueToHtml = await showHTMLDisclaimer();
Expand Down Expand Up @@ -656,7 +661,10 @@ const Playground: React.FC<PlaygroundProps> = ({ workspaceLocation = 'playground
}

// (TEMP) Remove tabs for fullJS until support is integrated
if (props.playgroundSourceChapter === Chapter.FULL_JS) {
if (
props.playgroundSourceChapter === Chapter.FULL_JS ||
props.playgroundSourceChapter === Chapter.FULL_TS
) {
return [...tabs, dataVisualizerTab];
}

Expand Down
Loading

0 comments on commit cec8f52

Please sign in to comment.