@codecademy/codebytes 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/MonacoEditor/colorsDark.d.ts +32 -0
- package/{src/MonacoEditor/colorsDark.ts → dist/MonacoEditor/colorsDark.js} +8 -16
- package/dist/MonacoEditor/index.d.ts +7 -0
- package/dist/MonacoEditor/index.js +42 -0
- package/dist/MonacoEditor/theme.d.ts +2 -0
- package/dist/MonacoEditor/theme.js +122 -0
- package/dist/MonacoEditor/types.d.ts +1 -0
- package/dist/MonacoEditor/types.js +0 -0
- package/dist/__tests__/codebyte-test.d.ts +1 -0
- package/dist/__tests__/codebyte-test.js +195 -0
- package/dist/__tests__/editor-test.d.ts +1 -0
- package/dist/__tests__/editor-test.js +147 -0
- package/dist/__tests__/helpers-test.d.ts +1 -0
- package/dist/__tests__/helpers-test.js +117 -0
- package/dist/__tests__/language-selection-test.d.ts +1 -0
- package/dist/__tests__/language-selection-test.js +15 -0
- package/dist/__tests__/mocks.d.ts +0 -0
- package/dist/__tests__/mocks.js +13 -0
- package/dist/api.d.ts +12 -0
- package/dist/api.js +39 -0
- package/dist/codeByteEditor.d.ts +13 -0
- package/dist/codeByteEditor.js +133 -0
- package/dist/consts.d.ts +23 -0
- package/dist/consts.js +34 -0
- package/dist/drawers.d.ts +6 -0
- package/dist/drawers.js +148 -0
- package/dist/editor.d.ts +13 -0
- package/dist/editor.js +189 -0
- package/dist/helpers/index.d.ts +15 -0
- package/dist/helpers/index.js +29 -0
- package/{src/index.ts → dist/index.d.ts} +0 -0
- package/dist/index.js +2 -0
- package/dist/languageSelection.d.ts +6 -0
- package/dist/languageSelection.js +22 -0
- package/dist/libs/eventTracking.d.ts +1 -0
- package/dist/libs/eventTracking.js +11 -0
- package/{src → dist}/theme.d.ts +0 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.js +0 -0
- package/package.json +16 -8
- package/babel.config.js +0 -15
- package/jest.config.js +0 -1
- package/src/MonacoEditor/index.tsx +0 -56
- package/src/MonacoEditor/theme.ts +0 -64
- package/src/MonacoEditor/types.ts +0 -1
- package/src/__tests__/codebyte-test.tsx +0 -197
- package/src/__tests__/editor-test.tsx +0 -113
- package/src/__tests__/helpers-test.tsx +0 -121
- package/src/__tests__/language-selection-test.tsx +0 -14
- package/src/__tests__/mocks.ts +0 -8
- package/src/api.ts +0 -28
- package/src/codeByteEditor.tsx +0 -118
- package/src/consts.ts +0 -64
- package/src/drawers.tsx +0 -132
- package/src/editor.tsx +0 -160
- package/src/helpers/index.ts +0 -41
- package/src/languageSelection.tsx +0 -26
- package/src/libs/eventTracking.ts +0 -18
- package/src/types.ts +0 -15
- package/tsconfig.json +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
### [0.6.3](https://github.com/Codecademy/client-modules/compare/@codecademy/codebytes@0.6.2...@codecademy/codebytes@0.6.3) (2022-02-02)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @codecademy/codebytes
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### [0.6.2](https://github.com/Codecademy/client-modules/compare/@codecademy/codebytes@0.6.1...@codecademy/codebytes@0.6.2) (2022-02-02)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @codecademy/codebytes
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
### [0.6.1](https://github.com/Codecademy/client-modules/compare/@codecademy/codebytes@0.6.0...@codecademy/codebytes@0.6.1) (2022-02-01)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @codecademy/codebytes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const syntax: {
|
|
2
|
+
attribute: "#b4d353";
|
|
3
|
+
annotation: "#e85d7f";
|
|
4
|
+
atom: "#cc7bc2";
|
|
5
|
+
basic: "#ffffff";
|
|
6
|
+
comment: "#939598";
|
|
7
|
+
constant: "#ff8973";
|
|
8
|
+
decoration: "#e85d7f";
|
|
9
|
+
invalid: "#e85d7f";
|
|
10
|
+
key: "#83fff5";
|
|
11
|
+
keyword: "#b3ccff";
|
|
12
|
+
number: "#e85d7f";
|
|
13
|
+
operator: "#e85d7f";
|
|
14
|
+
predefined: "#ffffff";
|
|
15
|
+
property: "#e85d7f";
|
|
16
|
+
regexp: "#b4d353";
|
|
17
|
+
string: "#ffe083";
|
|
18
|
+
tag: "#e85d7f";
|
|
19
|
+
text: "#ff8973";
|
|
20
|
+
value: "#ffe083";
|
|
21
|
+
variable: "#b4d353";
|
|
22
|
+
};
|
|
23
|
+
export declare const ui: {
|
|
24
|
+
background: string;
|
|
25
|
+
text: "#ffffff";
|
|
26
|
+
indent: {
|
|
27
|
+
active: string;
|
|
28
|
+
inactive: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare type SyntaxColors = typeof syntax;
|
|
32
|
+
export declare type UIColors = typeof ui;
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
// This file is part of the Codebytes MVP and only includes basic configuration around theming for the SimpleMonacoEditor component
|
|
3
3
|
// We are working on a monaco package in client-modules that has more configuration around themes and languages
|
|
4
4
|
// Monaco as a shared package RFC https://www.notion.so/codecademy/Monaco-editor-as-a-shared-package-1f4484db165b4abc8394c3556451ef6a
|
|
5
|
-
|
|
6
5
|
import { colors, editorColors } from '@codecademy/gamut-styles';
|
|
7
|
-
|
|
8
|
-
const darkTheme = {
|
|
6
|
+
var darkTheme = {
|
|
9
7
|
blue: editorColors.blue,
|
|
10
8
|
deepPurple: editorColors.deepPurple,
|
|
11
9
|
gray: editorColors.gray,
|
|
@@ -14,10 +12,9 @@ const darkTheme = {
|
|
|
14
12
|
purple: editorColors.purple,
|
|
15
13
|
red: editorColors.red,
|
|
16
14
|
white: colors.white,
|
|
17
|
-
yellow: editorColors.yellow
|
|
15
|
+
yellow: editorColors.yellow
|
|
18
16
|
};
|
|
19
|
-
|
|
20
|
-
export const syntax = {
|
|
17
|
+
export var syntax = {
|
|
21
18
|
attribute: darkTheme.green,
|
|
22
19
|
annotation: darkTheme.red,
|
|
23
20
|
atom: darkTheme.deepPurple,
|
|
@@ -37,18 +34,13 @@ export const syntax = {
|
|
|
37
34
|
tag: darkTheme.red,
|
|
38
35
|
text: darkTheme.orange,
|
|
39
36
|
value: darkTheme.yellow,
|
|
40
|
-
variable: darkTheme.green
|
|
37
|
+
variable: darkTheme.green
|
|
41
38
|
};
|
|
42
|
-
|
|
43
|
-
export const ui = {
|
|
39
|
+
export var ui = {
|
|
44
40
|
background: '#211E2F',
|
|
45
41
|
text: darkTheme.white,
|
|
46
42
|
indent: {
|
|
47
43
|
active: '#393b41',
|
|
48
|
-
inactive: '#494b51'
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export type SyntaxColors = typeof syntax;
|
|
53
|
-
|
|
54
|
-
export type UIColors = typeof ui;
|
|
44
|
+
inactive: '#494b51'
|
|
45
|
+
}
|
|
46
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// DO NOT CHANGE ANYTHING HERE
|
|
2
|
+
// This component is part of the Codebytes MVP and only includes basic configuration around theming
|
|
3
|
+
// We are working on a monaco package in client-modules that has more configuration around themes and languages
|
|
4
|
+
// Monaco as a shared package RFC https://www.notion.so/codecademy/Monaco-editor-as-a-shared-package-1f4484db165b4abc8394c3556451ef6a
|
|
5
|
+
import ReactMonacoEditor from '@monaco-editor/react';
|
|
6
|
+
import React, { useCallback, useRef } from 'react';
|
|
7
|
+
import ResizeObserver from 'react-resize-observer';
|
|
8
|
+
import { dark } from './theme';
|
|
9
|
+
export var SimpleMonacoEditor = function SimpleMonacoEditor(_ref) {
|
|
10
|
+
var value = _ref.value,
|
|
11
|
+
language = _ref.language,
|
|
12
|
+
onChange = _ref.onChange;
|
|
13
|
+
var editorRef = useRef(null);
|
|
14
|
+
var editorWillMount = useCallback(function (editor, monaco) {
|
|
15
|
+
editorRef.current = editor;
|
|
16
|
+
monaco.editor.defineTheme('dark', dark);
|
|
17
|
+
monaco.editor.setTheme('dark');
|
|
18
|
+
}, []);
|
|
19
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ResizeObserver, {
|
|
20
|
+
onResize: function onResize(_ref2) {
|
|
21
|
+
var _editorRef$current;
|
|
22
|
+
|
|
23
|
+
var height = _ref2.height,
|
|
24
|
+
width = _ref2.width;
|
|
25
|
+
(_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 ? void 0 : _editorRef$current.layout({
|
|
26
|
+
height: height,
|
|
27
|
+
width: width
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}), /*#__PURE__*/React.createElement(ReactMonacoEditor, {
|
|
31
|
+
onMount: editorWillMount,
|
|
32
|
+
onChange: onChange,
|
|
33
|
+
options: {
|
|
34
|
+
minimap: {
|
|
35
|
+
enabled: false
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
theme: "vs-dark",
|
|
39
|
+
value: value,
|
|
40
|
+
language: language
|
|
41
|
+
}));
|
|
42
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// DO NOT CHANGE ANYTHING HERE
|
|
2
|
+
// This file is part of the Codebytes MVP and only includes basic configuration around theming for the SimpleMonacoEditor component
|
|
3
|
+
// We are working on a monaco package in client-modules that has more configuration around themes and languages
|
|
4
|
+
// Monaco as a shared package RFC https://www.notion.so/codecademy/Monaco-editor-as-a-shared-package-1f4484db165b4abc8394c3556451ef6a
|
|
5
|
+
import * as darkColors from './colorsDark';
|
|
6
|
+
|
|
7
|
+
var c = function c(color) {
|
|
8
|
+
return color.substr(1);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
var theme = function theme(_ref) {
|
|
12
|
+
var ui = _ref.ui,
|
|
13
|
+
syntax = _ref.syntax;
|
|
14
|
+
return {
|
|
15
|
+
base: 'vs-dark',
|
|
16
|
+
inherit: true,
|
|
17
|
+
rules: [// Base
|
|
18
|
+
{
|
|
19
|
+
token: '',
|
|
20
|
+
foreground: c(syntax.basic)
|
|
21
|
+
}, {
|
|
22
|
+
token: 'regexp',
|
|
23
|
+
foreground: c(syntax.regexp)
|
|
24
|
+
}, {
|
|
25
|
+
token: 'annotation',
|
|
26
|
+
foreground: c(syntax.annotation)
|
|
27
|
+
}, {
|
|
28
|
+
token: 'type',
|
|
29
|
+
foreground: c(syntax.annotation)
|
|
30
|
+
}, {
|
|
31
|
+
token: 'doctype',
|
|
32
|
+
foreground: c(syntax.comment)
|
|
33
|
+
}, {
|
|
34
|
+
token: 'delimiter',
|
|
35
|
+
foreground: c(syntax.decoration)
|
|
36
|
+
}, {
|
|
37
|
+
token: 'invalid',
|
|
38
|
+
foreground: c(syntax.invalid)
|
|
39
|
+
}, {
|
|
40
|
+
token: 'emphasis',
|
|
41
|
+
fontStyle: 'italic'
|
|
42
|
+
}, {
|
|
43
|
+
token: 'strong',
|
|
44
|
+
fontStyle: 'bold'
|
|
45
|
+
}, {
|
|
46
|
+
token: 'variable',
|
|
47
|
+
foreground: c(syntax.variable)
|
|
48
|
+
}, {
|
|
49
|
+
token: 'variable.predefined',
|
|
50
|
+
foreground: c(syntax.variable)
|
|
51
|
+
}, {
|
|
52
|
+
token: 'constant',
|
|
53
|
+
foreground: c(syntax.constant)
|
|
54
|
+
}, {
|
|
55
|
+
token: 'comment',
|
|
56
|
+
foreground: c(syntax.comment)
|
|
57
|
+
}, {
|
|
58
|
+
token: 'number',
|
|
59
|
+
foreground: c(syntax.number)
|
|
60
|
+
}, {
|
|
61
|
+
token: 'number.hex',
|
|
62
|
+
foreground: c(syntax.number)
|
|
63
|
+
}, {
|
|
64
|
+
token: 'keyword.directive',
|
|
65
|
+
foreground: c(syntax.comment)
|
|
66
|
+
}, {
|
|
67
|
+
token: 'include',
|
|
68
|
+
foreground: c(syntax.comment)
|
|
69
|
+
}, {
|
|
70
|
+
token: 'key',
|
|
71
|
+
foreground: c(syntax.property)
|
|
72
|
+
}, {
|
|
73
|
+
token: 'attribute.name',
|
|
74
|
+
foreground: c(syntax.attribute)
|
|
75
|
+
}, {
|
|
76
|
+
token: 'attribute.name-numeric',
|
|
77
|
+
foreground: c(syntax.string)
|
|
78
|
+
}, {
|
|
79
|
+
token: 'attribute.value',
|
|
80
|
+
foreground: c(syntax.property)
|
|
81
|
+
}, {
|
|
82
|
+
token: 'attribute.value.number',
|
|
83
|
+
foreground: c(syntax.number)
|
|
84
|
+
}, {
|
|
85
|
+
token: 'string',
|
|
86
|
+
foreground: c(syntax.string)
|
|
87
|
+
}, {
|
|
88
|
+
token: 'string.yaml',
|
|
89
|
+
foreground: c(syntax.string)
|
|
90
|
+
}, {
|
|
91
|
+
token: 'tag',
|
|
92
|
+
foreground: c(syntax.tag)
|
|
93
|
+
}, {
|
|
94
|
+
token: 'tag.id.jade',
|
|
95
|
+
foreground: c(syntax.tag)
|
|
96
|
+
}, {
|
|
97
|
+
token: 'tag.class.jade',
|
|
98
|
+
foreground: c(syntax.tag)
|
|
99
|
+
}, {
|
|
100
|
+
token: 'metatag',
|
|
101
|
+
foreground: c(syntax.comment)
|
|
102
|
+
}, {
|
|
103
|
+
token: 'attribute.value.unit',
|
|
104
|
+
foreground: c(syntax.string)
|
|
105
|
+
}, {
|
|
106
|
+
token: 'keyword',
|
|
107
|
+
foreground: c(syntax.keyword)
|
|
108
|
+
}, {
|
|
109
|
+
token: 'keyword.flow',
|
|
110
|
+
foreground: c(syntax.keyword)
|
|
111
|
+
}],
|
|
112
|
+
colors: {
|
|
113
|
+
'editor.background': ui.background,
|
|
114
|
+
'editor.foreground': ui.text,
|
|
115
|
+
'editorIndentGuide.background': ui.indent.inactive,
|
|
116
|
+
'editorIndentGuide.activeBackground': ui.indent.active,
|
|
117
|
+
'editorWhitespace.foreground': syntax.comment
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export var dark = theme(darkColors);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare type Monaco = typeof import('monaco-editor');
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './mocks';
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
2
|
+
|
|
3
|
+
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
4
|
+
|
|
5
|
+
import './mocks';
|
|
6
|
+
import { setupRtl } from '@codecademy/gamut-tests';
|
|
7
|
+
import userEvent from '@testing-library/user-event';
|
|
8
|
+
import { encode } from 'js-base64';
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import { CodeByteEditor } from '..';
|
|
11
|
+
import { helloWorld, validLanguages } from '../consts';
|
|
12
|
+
import { trackClick } from '../helpers';
|
|
13
|
+
import { trackUserImpression } from '../libs/eventTracking';
|
|
14
|
+
var mockEditorTestId = 'mock-editor-test-id'; // This is a super simplified mock capable of render value and trigger onChange.
|
|
15
|
+
|
|
16
|
+
jest.mock('../MonacoEditor', function () {
|
|
17
|
+
return {
|
|
18
|
+
SimpleMonacoEditor: function SimpleMonacoEditor(_ref) {
|
|
19
|
+
var value = _ref.value,
|
|
20
|
+
_onChange = _ref.onChange;
|
|
21
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, value, /*#__PURE__*/React.createElement("input", {
|
|
22
|
+
"data-testid": mockEditorTestId,
|
|
23
|
+
type: "text",
|
|
24
|
+
onChange: function onChange(e) {
|
|
25
|
+
_onChange === null || _onChange === void 0 ? void 0 : _onChange(e.target.value);
|
|
26
|
+
},
|
|
27
|
+
value: value
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
var renderWrapper = setupRtl(CodeByteEditor, {});
|
|
33
|
+
|
|
34
|
+
var renderWrapperWith = function renderWrapperWith(_ref2) {
|
|
35
|
+
var mode = _ref2.mode,
|
|
36
|
+
rest = _objectWithoutProperties(_ref2, ["mode"]);
|
|
37
|
+
|
|
38
|
+
var url = new URL(window.location.href);
|
|
39
|
+
var text = rest.text,
|
|
40
|
+
language = rest.language;
|
|
41
|
+
|
|
42
|
+
if (text) {
|
|
43
|
+
url.searchParams.set('text', encode(text));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (language) {
|
|
47
|
+
url.searchParams.set('lang', language);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
url.searchParams.set('client-name', 'forum');
|
|
51
|
+
url.searchParams.set('page', 'https://discuss.codecademy.com/some-interesting/post');
|
|
52
|
+
|
|
53
|
+
if (mode) {
|
|
54
|
+
url.searchParams.set('mode', mode);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
window.history.replaceState({}, '', url.toString());
|
|
58
|
+
return renderWrapper(rest);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
describe('CodeBytes', function () {
|
|
62
|
+
var initialUrl = window.location.href;
|
|
63
|
+
afterEach(function () {
|
|
64
|
+
window.history.replaceState(null, '', initialUrl);
|
|
65
|
+
trackClick.mockReset();
|
|
66
|
+
trackUserImpression.mockReset();
|
|
67
|
+
});
|
|
68
|
+
it('has a language-specific "hello world" program defined for each language', function () {
|
|
69
|
+
validLanguages.forEach(function (language) {
|
|
70
|
+
expect(helloWorld[language]).toBeDefined();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
it('initializes with a language-specific "hello world" program when there is no language prop', function () {
|
|
74
|
+
var _renderWrapper = renderWrapper(),
|
|
75
|
+
view = _renderWrapper.view;
|
|
76
|
+
|
|
77
|
+
var selectedLanguage = view.getByRole('combobox');
|
|
78
|
+
userEvent.selectOptions(selectedLanguage, ['javascript']);
|
|
79
|
+
view.getByText(helloWorld.javascript);
|
|
80
|
+
});
|
|
81
|
+
it('initializes with a language-specific "hello world" program when there is a language prop but no text prop', function () {
|
|
82
|
+
var _renderWrapper2 = renderWrapper({
|
|
83
|
+
language: 'javascript'
|
|
84
|
+
}),
|
|
85
|
+
view = _renderWrapper2.view;
|
|
86
|
+
|
|
87
|
+
view.getByText(helloWorld.javascript);
|
|
88
|
+
});
|
|
89
|
+
it('initializes with deserialized text when there is a text prop but no language prop', function () {
|
|
90
|
+
var testString = 'yes hello';
|
|
91
|
+
|
|
92
|
+
var _renderWrapper3 = renderWrapper({
|
|
93
|
+
text: testString
|
|
94
|
+
}),
|
|
95
|
+
view = _renderWrapper3.view;
|
|
96
|
+
|
|
97
|
+
var selectedLanguage = view.getByRole('combobox');
|
|
98
|
+
userEvent.selectOptions(selectedLanguage, ['javascript']);
|
|
99
|
+
view.getByText(testString);
|
|
100
|
+
});
|
|
101
|
+
it('initializes with deserialized text when there is both a language and text prop', function () {
|
|
102
|
+
var testString = 'yes hello';
|
|
103
|
+
|
|
104
|
+
var _renderWrapper4 = renderWrapper({
|
|
105
|
+
text: testString,
|
|
106
|
+
language: 'javascript'
|
|
107
|
+
}),
|
|
108
|
+
view = _renderWrapper4.view;
|
|
109
|
+
|
|
110
|
+
view.getByText(testString);
|
|
111
|
+
});
|
|
112
|
+
describe('Change Handlers', function () {
|
|
113
|
+
it('triggers onEdit on text edit', function () {
|
|
114
|
+
var onEdit = jest.fn();
|
|
115
|
+
|
|
116
|
+
var _renderWrapper5 = renderWrapper({
|
|
117
|
+
text: '',
|
|
118
|
+
language: 'javascript',
|
|
119
|
+
onEdit: onEdit
|
|
120
|
+
}),
|
|
121
|
+
view = _renderWrapper5.view;
|
|
122
|
+
|
|
123
|
+
var editor = view.getByTestId(mockEditorTestId);
|
|
124
|
+
userEvent.type(editor, 'dog');
|
|
125
|
+
expect(onEdit).toHaveBeenCalledTimes(3);
|
|
126
|
+
expect(onEdit).toHaveBeenLastCalledWith('dog', 'javascript');
|
|
127
|
+
});
|
|
128
|
+
it('triggers onLanguageChange on language selection', function () {
|
|
129
|
+
var onLanguageChange = jest.fn();
|
|
130
|
+
|
|
131
|
+
var _renderWrapper6 = renderWrapper({
|
|
132
|
+
onLanguageChange: onLanguageChange
|
|
133
|
+
}),
|
|
134
|
+
view = _renderWrapper6.view;
|
|
135
|
+
|
|
136
|
+
var selectedLanguage = view.getByRole('combobox');
|
|
137
|
+
userEvent.selectOptions(selectedLanguage, ['javascript']);
|
|
138
|
+
expect(onLanguageChange).toHaveBeenCalledWith("console.log('Hello world!');", 'javascript');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
describe('Tracking', function () {
|
|
142
|
+
it('triggers trackClick on clicking the logo', function () {
|
|
143
|
+
var _renderWrapper7 = renderWrapper({}),
|
|
144
|
+
view = _renderWrapper7.view;
|
|
145
|
+
|
|
146
|
+
var logo = view.getByLabelText('visit codecademy.com');
|
|
147
|
+
userEvent.click(logo);
|
|
148
|
+
expect(trackClick).toHaveBeenCalledWith('logo');
|
|
149
|
+
});
|
|
150
|
+
it('triggers trackClick on language selection', function () {
|
|
151
|
+
var _renderWrapper8 = renderWrapper(),
|
|
152
|
+
view = _renderWrapper8.view;
|
|
153
|
+
|
|
154
|
+
var selectedLanguage = view.getByRole('combobox');
|
|
155
|
+
userEvent.selectOptions(selectedLanguage, ['javascript']);
|
|
156
|
+
expect(trackClick).toHaveBeenCalledWith('lang_select');
|
|
157
|
+
});
|
|
158
|
+
it('triggers trackClick for the first edit in view mode', function () {
|
|
159
|
+
var testString = 'original-value';
|
|
160
|
+
|
|
161
|
+
var _renderWrapper9 = renderWrapper({
|
|
162
|
+
text: testString,
|
|
163
|
+
language: 'javascript'
|
|
164
|
+
}),
|
|
165
|
+
view = _renderWrapper9.view;
|
|
166
|
+
|
|
167
|
+
var editor = view.getByTestId(mockEditorTestId);
|
|
168
|
+
userEvent.type(editor, 'd');
|
|
169
|
+
expect(trackClick).toHaveBeenCalledWith('edit');
|
|
170
|
+
});
|
|
171
|
+
it('triggers trackUserImpression for view mode', function () {
|
|
172
|
+
renderWrapperWith({
|
|
173
|
+
text: 'some-value',
|
|
174
|
+
language: 'javascript'
|
|
175
|
+
});
|
|
176
|
+
expect(trackUserImpression).toHaveBeenCalledWith({
|
|
177
|
+
page_name: 'forum',
|
|
178
|
+
context: 'https://discuss.codecademy.com/some-interesting/post',
|
|
179
|
+
target: 'codebyte'
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
it('triggers trackUserImpression for compose mode', function () {
|
|
183
|
+
renderWrapperWith({
|
|
184
|
+
text: 'some-value',
|
|
185
|
+
language: 'javascript',
|
|
186
|
+
mode: 'compose'
|
|
187
|
+
});
|
|
188
|
+
expect(trackUserImpression).toHaveBeenCalledWith({
|
|
189
|
+
page_name: 'forum_compose',
|
|
190
|
+
context: 'https://discuss.codecademy.com/some-interesting/post',
|
|
191
|
+
target: 'codebyte'
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './mocks';
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
2
|
+
|
|
3
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
4
|
+
|
|
5
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
6
|
+
|
|
7
|
+
import './mocks';
|
|
8
|
+
import { setupRtl } from '@codecademy/gamut-tests';
|
|
9
|
+
import { act } from '@testing-library/react';
|
|
10
|
+
import userEvent from '@testing-library/user-event';
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { Editor } from '../editor';
|
|
13
|
+
import { trackClick } from '../helpers';
|
|
14
|
+
jest.mock('../MonacoEditor', function () {
|
|
15
|
+
return {
|
|
16
|
+
SimpleMonacoEditor: function SimpleMonacoEditor(_ref) {
|
|
17
|
+
var value = _ref.value;
|
|
18
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, value);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
var renderWrapper = setupRtl(Editor, {
|
|
23
|
+
hideCopyButton: false,
|
|
24
|
+
language: 'javascript',
|
|
25
|
+
text: 'hello world',
|
|
26
|
+
onChange: jest.fn(),
|
|
27
|
+
snippetsBaseUrl: ''
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(navigator, 'clipboard', {
|
|
30
|
+
value: {
|
|
31
|
+
writeText: jest.fn().mockImplementation(function () {
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
describe('Editor', function () {
|
|
37
|
+
global.fetch = jest.fn();
|
|
38
|
+
afterEach(function () {
|
|
39
|
+
global.fetch.mockClear();
|
|
40
|
+
});
|
|
41
|
+
it('shows a prompt tooltip when the CodeByte has __not__ been copied via the button', function () {
|
|
42
|
+
var _renderWrapper = renderWrapper(),
|
|
43
|
+
view = _renderWrapper.view;
|
|
44
|
+
|
|
45
|
+
expect(view.queryByTestId('copy-confirmation-tooltip')).toBeFalsy();
|
|
46
|
+
view.getByTestId('copy-prompt-tooltip');
|
|
47
|
+
});
|
|
48
|
+
it('shows a confirmation tooltip when the CodeByte has been copied via the button', function () {
|
|
49
|
+
var _renderWrapper2 = renderWrapper(),
|
|
50
|
+
view = _renderWrapper2.view;
|
|
51
|
+
|
|
52
|
+
var copyBtn = view.getByTestId('copy-codebyte-btn');
|
|
53
|
+
userEvent.click(copyBtn);
|
|
54
|
+
expect(view.queryByTestId('copy-prompt-tooltip')).toBeFalsy();
|
|
55
|
+
view.getByTestId('copy-confirmation-tooltip');
|
|
56
|
+
});
|
|
57
|
+
it('hides the copy codebyte button if hideCopyButton prop is true"', function () {
|
|
58
|
+
var _renderWrapper3 = renderWrapper({
|
|
59
|
+
hideCopyButton: true
|
|
60
|
+
}),
|
|
61
|
+
view = _renderWrapper3.view;
|
|
62
|
+
|
|
63
|
+
expect(view.queryByTestId('copy-codebyte-btn')).toBeNull();
|
|
64
|
+
});
|
|
65
|
+
it('shows the copy codebyte button if hideCopyButton prop is not set', function () {
|
|
66
|
+
var _renderWrapper4 = renderWrapper(),
|
|
67
|
+
view = _renderWrapper4.view;
|
|
68
|
+
|
|
69
|
+
view.getByTestId('copy-codebyte-btn');
|
|
70
|
+
});
|
|
71
|
+
describe('Change Handlers', function () {
|
|
72
|
+
it('triggers onCopy upon clicking the copy button', function () {
|
|
73
|
+
var onCopy = jest.fn();
|
|
74
|
+
|
|
75
|
+
var _renderWrapper5 = renderWrapper({
|
|
76
|
+
onCopy: onCopy
|
|
77
|
+
}),
|
|
78
|
+
view = _renderWrapper5.view;
|
|
79
|
+
|
|
80
|
+
var copyButton = view.getByTestId('copy-codebyte-btn');
|
|
81
|
+
userEvent.click(copyButton);
|
|
82
|
+
expect(onCopy).toHaveBeenCalled();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe('Tracking', function () {
|
|
86
|
+
it('tracks clicks on the run button', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
87
|
+
var _renderWrapper6, view, runButton;
|
|
88
|
+
|
|
89
|
+
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
90
|
+
while (1) {
|
|
91
|
+
switch (_context2.prev = _context2.next) {
|
|
92
|
+
case 0:
|
|
93
|
+
global.fetch.mockResolvedValue({
|
|
94
|
+
json: function json() {
|
|
95
|
+
return Promise.resolve({
|
|
96
|
+
stderr: [],
|
|
97
|
+
exit_code: 0,
|
|
98
|
+
stdout: ''
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
_renderWrapper6 = renderWrapper({
|
|
103
|
+
onChange: jest.fn(),
|
|
104
|
+
text: 'test',
|
|
105
|
+
language: 'javascript'
|
|
106
|
+
}), view = _renderWrapper6.view;
|
|
107
|
+
runButton = view.getByText('Run');
|
|
108
|
+
_context2.next = 5;
|
|
109
|
+
return act( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
110
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
111
|
+
while (1) {
|
|
112
|
+
switch (_context.prev = _context.next) {
|
|
113
|
+
case 0:
|
|
114
|
+
userEvent.click(runButton);
|
|
115
|
+
|
|
116
|
+
case 1:
|
|
117
|
+
case "end":
|
|
118
|
+
return _context.stop();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}, _callee);
|
|
122
|
+
})));
|
|
123
|
+
|
|
124
|
+
case 5:
|
|
125
|
+
expect(trackClick).toHaveBeenCalledWith('run');
|
|
126
|
+
|
|
127
|
+
case 6:
|
|
128
|
+
case "end":
|
|
129
|
+
return _context2.stop();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}, _callee2);
|
|
133
|
+
})));
|
|
134
|
+
it('tracks clicks on the copy codebyte button', function () {
|
|
135
|
+
var _renderWrapper7 = renderWrapper({
|
|
136
|
+
onChange: jest.fn(),
|
|
137
|
+
text: 'test',
|
|
138
|
+
language: 'javascript'
|
|
139
|
+
}),
|
|
140
|
+
view = _renderWrapper7.view;
|
|
141
|
+
|
|
142
|
+
var copyButton = view.getByTestId('copy-codebyte-btn');
|
|
143
|
+
userEvent.click(copyButton);
|
|
144
|
+
expect(trackClick).toHaveBeenCalledWith('copy');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|