fix(edge): get client app to work on edge (#35040)
This commit is contained in:
committed by
mrugesh mohapatra
parent
e71b82c7a4
commit
cc6e1fdbf4
@ -3,18 +3,12 @@ import jQuery from 'jquery';
|
|||||||
|
|
||||||
window.$ = jQuery;
|
window.$ = jQuery;
|
||||||
|
|
||||||
const testId = 'fcc-test-frame';
|
|
||||||
if (window.frameElement && window.frameElement.id === testId) {
|
|
||||||
document.addEventListener('DOMContentLoaded', initTestFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For tests in CI.
|
|
||||||
document.__initTestFrame = initTestFrame;
|
document.__initTestFrame = initTestFrame;
|
||||||
|
|
||||||
async function initTestFrame() {
|
async function initTestFrame(e = {}) {
|
||||||
const code = (document.__source || '').slice(0);
|
const code = (e.code || '').slice(0);
|
||||||
if (!document.__getUserInput) {
|
if (!e.getUserInput) {
|
||||||
document.__getUserInput = () => code;
|
e.getUserInput = () => code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
@ -43,7 +37,7 @@ async function initTestFrame() {
|
|||||||
/* eslint-enable no-unused-vars */
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
let Enzyme;
|
let Enzyme;
|
||||||
if (document.__loadEnzyme) {
|
if (e.loadEnzyme) {
|
||||||
let Adapter16;
|
let Adapter16;
|
||||||
/* eslint-disable no-inline-comments */
|
/* eslint-disable no-inline-comments */
|
||||||
[{ default: Enzyme }, { default: Adapter16 }] = await Promise.all([
|
[{ default: Enzyme }, { default: Adapter16 }] = await Promise.all([
|
||||||
@ -66,7 +60,7 @@ async function initTestFrame() {
|
|||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
const test = eval(testString);
|
const test = eval(testString);
|
||||||
if (typeof test === 'function') {
|
if (typeof test === 'function') {
|
||||||
await test(document.__getUserInput);
|
await test(e.getUserInput);
|
||||||
}
|
}
|
||||||
return { pass: true };
|
return { pass: true };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -81,7 +75,4 @@ async function initTestFrame() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// notify that the window methods are ready to run
|
|
||||||
document.__frameReady();
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
// eslint-disable-next-line no-undef
|
// work around for SASS error in Edge
|
||||||
importScripts('/js/sass.sync.js');
|
// https://github.com/medialize/sass.js/issues/96#issuecomment-424386171
|
||||||
|
if (!self.crypto) {
|
||||||
|
self.crypto = {
|
||||||
|
getRandomValues: function(array) {
|
||||||
|
for (var i = 0, l = array.length; i < l; i++) {
|
||||||
|
array[i] = Math.floor(Math.random() * 256);
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.importScripts('/js/sass.sync.js');
|
||||||
|
|
||||||
self.onmessage = e => {
|
self.onmessage = e => {
|
||||||
const data = e.data;
|
const data = e.data;
|
||||||
@ -11,3 +23,5 @@ self.onmessage = e => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.postMessage({ type: 'contentLoaded' });
|
||||||
|
@ -48,3 +48,5 @@ self.onmessage = async e => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.postMessage({ type: 'contentLoaded' });
|
||||||
|
@ -60,6 +60,44 @@ const propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class CompletionModal extends Component {
|
export class CompletionModal extends Component {
|
||||||
|
state = {
|
||||||
|
downloadURL: null
|
||||||
|
};
|
||||||
|
|
||||||
|
static getDerivedStateFromProps(props, state) {
|
||||||
|
const { files, isOpen } = props;
|
||||||
|
if (!isOpen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { downloadURL } = state;
|
||||||
|
if (downloadURL) {
|
||||||
|
URL.revokeObjectURL(downloadURL);
|
||||||
|
}
|
||||||
|
let newURL = null;
|
||||||
|
if (Object.keys(files).length) {
|
||||||
|
const filesForDownload = Object.keys(files)
|
||||||
|
.map(key => files[key])
|
||||||
|
.reduce(
|
||||||
|
(allFiles, { path, contents }) => ({
|
||||||
|
...allFiles,
|
||||||
|
[path]: contents
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const blob = new Blob([JSON.stringify(filesForDownload, null, 2)], {
|
||||||
|
type: 'text/json'
|
||||||
|
});
|
||||||
|
newURL = URL.createObjectURL(blob);
|
||||||
|
}
|
||||||
|
return { downloadURL: newURL };
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.state.downloadURL) {
|
||||||
|
URL.revokeObjectURL(this.state.downloadURL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
close,
|
close,
|
||||||
@ -67,22 +105,11 @@ export class CompletionModal extends Component {
|
|||||||
submitChallenge,
|
submitChallenge,
|
||||||
handleKeypress,
|
handleKeypress,
|
||||||
message,
|
message,
|
||||||
files = {},
|
|
||||||
title
|
title
|
||||||
} = this.props;
|
} = this.props;
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
ga.modalview('/completion-modal');
|
ga.modalview('/completion-modal');
|
||||||
}
|
}
|
||||||
const showDownloadButton = Object.keys(files).length;
|
|
||||||
const filesForDownload = Object.keys(files)
|
|
||||||
.map(key => files[key])
|
|
||||||
.reduce(
|
|
||||||
(allFiles, { path, contents }) => ({
|
|
||||||
...allFiles,
|
|
||||||
[path]: contents
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
const dashedName = dasherize(title);
|
const dashedName = dasherize(title);
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -112,18 +139,17 @@ export class CompletionModal extends Component {
|
|||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
onClick={submitChallenge}
|
onClick={submitChallenge}
|
||||||
>
|
>
|
||||||
Submit and go to next challenge <span className='hidden-xs'>(Ctrl + Enter)</span>
|
Submit and go to next challenge{' '}
|
||||||
|
<span className='hidden-xs'>(Ctrl + Enter)</span>
|
||||||
</Button>
|
</Button>
|
||||||
{showDownloadButton ? (
|
{this.state.downloadURL ? (
|
||||||
<Button
|
<Button
|
||||||
block={true}
|
block={true}
|
||||||
bsSize='lg'
|
bsSize='lg'
|
||||||
bsStyle='primary'
|
bsStyle='primary'
|
||||||
className='btn-primary-invert'
|
className='btn-primary-invert'
|
||||||
download={`${dashedName}.json`}
|
download={`${dashedName}.json`}
|
||||||
href={`data:text/json;charset=utf-8,${encodeURIComponent(
|
href={this.state.downloadURL}
|
||||||
JSON.stringify(filesForDownload)
|
|
||||||
)}`}
|
|
||||||
>
|
>
|
||||||
Download my solution
|
Download my solution
|
||||||
</Button>
|
</Button>
|
||||||
@ -137,4 +163,7 @@ export class CompletionModal extends Component {
|
|||||||
CompletionModal.displayName = 'CompletionModal';
|
CompletionModal.displayName = 'CompletionModal';
|
||||||
CompletionModal.propTypes = propTypes;
|
CompletionModal.propTypes = propTypes;
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(CompletionModal);
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(CompletionModal);
|
||||||
|
@ -72,15 +72,24 @@ const buildProxyConsole = proxyLogger => ctx => {
|
|||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
const writeTestDepsToDocument = frameReady => ctx => {
|
const initTestFrame = frameReady => ctx => {
|
||||||
const { sources, loadEnzyme } = ctx;
|
const contentLoaded = new Promise(resolve => {
|
||||||
// default for classic challenges
|
if (ctx.document.readyState === 'loading') {
|
||||||
// should not be used for modern
|
ctx.document.addEventListener('DOMContentLoaded', resolve);
|
||||||
ctx.document.__source = sources && 'index' in sources ? sources['index'] : '';
|
} else {
|
||||||
// provide the file name and get the original source
|
resolve();
|
||||||
ctx.document.__getUserInput = fileName => toString(sources[fileName]);
|
}
|
||||||
ctx.document.__frameReady = frameReady;
|
});
|
||||||
ctx.document.__loadEnzyme = loadEnzyme;
|
contentLoaded.then(async() => {
|
||||||
|
const { sources, loadEnzyme } = ctx;
|
||||||
|
// default for classic challenges
|
||||||
|
// should not be used for modern
|
||||||
|
const code = sources && 'index' in sources ? sources['index'] : '';
|
||||||
|
// provide the file name and get the original source
|
||||||
|
const getUserInput = fileName => toString(sources[fileName]);
|
||||||
|
await ctx.document.__initTestFrame({ code, getUserInput, loadEnzyme });
|
||||||
|
frameReady();
|
||||||
|
});
|
||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -107,7 +116,7 @@ export const createTestFramer = (document, frameReady, proxyConsole) =>
|
|||||||
flow(
|
flow(
|
||||||
createFrame(document, testId),
|
createFrame(document, testId),
|
||||||
mountFrame(document),
|
mountFrame(document),
|
||||||
writeTestDepsToDocument(frameReady),
|
writeContentToFrame,
|
||||||
buildProxyConsole(proxyConsole),
|
buildProxyConsole(proxyConsole),
|
||||||
writeContentToFrame
|
initTestFrame(frameReady)
|
||||||
);
|
);
|
||||||
|
@ -10,9 +10,17 @@ class WorkerExecutor {
|
|||||||
this.getWorker = this.getWorker.bind(this);
|
this.getWorker = this.getWorker.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
getWorker() {
|
async getWorker() {
|
||||||
if (this.worker === null) {
|
if (this.worker === null) {
|
||||||
this.worker = new Worker(`${this.location}${this.workerName}.js`);
|
this.worker = await new Promise((resolve, reject) => {
|
||||||
|
const worker = new Worker(`${this.location}${this.workerName}.js`);
|
||||||
|
worker.onmessage = e => {
|
||||||
|
if (e.data && e.data.type && e.data.type === 'contentLoaded') {
|
||||||
|
resolve(worker);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
worker.onerror = e => reject(e.message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.worker;
|
return this.worker;
|
||||||
@ -25,8 +33,8 @@ class WorkerExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(data, timeout = 1000) {
|
async execute(data, timeout = 1000) {
|
||||||
const worker = this.getWorker();
|
const worker = await this.getWorker();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Handle timeout
|
// Handle timeout
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
|
@ -291,11 +291,9 @@ async function createTestRunnerForDOMChallenge(
|
|||||||
await context.setContent(build);
|
await context.setContent(build);
|
||||||
await context.evaluate(
|
await context.evaluate(
|
||||||
async(sources, loadEnzyme) => {
|
async(sources, loadEnzyme) => {
|
||||||
document.__source = sources && 'index' in sources ? sources['index'] : '';
|
const code = sources && 'index' in sources ? sources['index'] : '';
|
||||||
document.__getUserInput = fileName => sources[fileName];
|
const getUserInput = fileName => sources[fileName];
|
||||||
document.__frameReady = () => {};
|
await document.__initTestFrame({ code, getUserInput, loadEnzyme });
|
||||||
document.__loadEnzyme = loadEnzyme;
|
|
||||||
await document.__initTestFrame();
|
|
||||||
},
|
},
|
||||||
sources,
|
sources,
|
||||||
loadEnzyme
|
loadEnzyme
|
||||||
|
Reference in New Issue
Block a user