fix(edge): get client app to work on edge (#35040)
parent
e71b82c7a4
commit
cc6e1fdbf4
|
@ -3,18 +3,12 @@ import jQuery from '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;
|
||||
|
||||
async function initTestFrame() {
|
||||
const code = (document.__source || '').slice(0);
|
||||
if (!document.__getUserInput) {
|
||||
document.__getUserInput = () => code;
|
||||
async function initTestFrame(e = {}) {
|
||||
const code = (e.code || '').slice(0);
|
||||
if (!e.getUserInput) {
|
||||
e.getUserInput = () => code;
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
@ -43,7 +37,7 @@ async function initTestFrame() {
|
|||
/* eslint-enable no-unused-vars */
|
||||
|
||||
let Enzyme;
|
||||
if (document.__loadEnzyme) {
|
||||
if (e.loadEnzyme) {
|
||||
let Adapter16;
|
||||
/* eslint-disable no-inline-comments */
|
||||
[{ default: Enzyme }, { default: Adapter16 }] = await Promise.all([
|
||||
|
@ -66,7 +60,7 @@ async function initTestFrame() {
|
|||
// eslint-disable-next-line no-eval
|
||||
const test = eval(testString);
|
||||
if (typeof test === 'function') {
|
||||
await test(document.__getUserInput);
|
||||
await test(e.getUserInput);
|
||||
}
|
||||
return { pass: true };
|
||||
} 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
|
||||
importScripts('/js/sass.sync.js');
|
||||
// work around for SASS error in Edge
|
||||
// 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 => {
|
||||
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 {
|
||||
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() {
|
||||
const {
|
||||
close,
|
||||
|
@ -67,22 +105,11 @@ export class CompletionModal extends Component {
|
|||
submitChallenge,
|
||||
handleKeypress,
|
||||
message,
|
||||
files = {},
|
||||
title
|
||||
} = this.props;
|
||||
if (isOpen) {
|
||||
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);
|
||||
return (
|
||||
<Modal
|
||||
|
@ -112,18 +139,17 @@ export class CompletionModal extends Component {
|
|||
bsStyle='primary'
|
||||
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>
|
||||
{showDownloadButton ? (
|
||||
{this.state.downloadURL ? (
|
||||
<Button
|
||||
block={true}
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
className='btn-primary-invert'
|
||||
download={`${dashedName}.json`}
|
||||
href={`data:text/json;charset=utf-8,${encodeURIComponent(
|
||||
JSON.stringify(filesForDownload)
|
||||
)}`}
|
||||
href={this.state.downloadURL}
|
||||
>
|
||||
Download my solution
|
||||
</Button>
|
||||
|
@ -137,4 +163,7 @@ export class CompletionModal extends Component {
|
|||
CompletionModal.displayName = 'CompletionModal';
|
||||
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;
|
||||
};
|
||||
|
||||
const writeTestDepsToDocument = frameReady => ctx => {
|
||||
const { sources, loadEnzyme } = ctx;
|
||||
// default for classic challenges
|
||||
// should not be used for modern
|
||||
ctx.document.__source = sources && 'index' in sources ? sources['index'] : '';
|
||||
// provide the file name and get the original source
|
||||
ctx.document.__getUserInput = fileName => toString(sources[fileName]);
|
||||
ctx.document.__frameReady = frameReady;
|
||||
ctx.document.__loadEnzyme = loadEnzyme;
|
||||
const initTestFrame = frameReady => ctx => {
|
||||
const contentLoaded = new Promise(resolve => {
|
||||
if (ctx.document.readyState === 'loading') {
|
||||
ctx.document.addEventListener('DOMContentLoaded', resolve);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -107,7 +116,7 @@ export const createTestFramer = (document, frameReady, proxyConsole) =>
|
|||
flow(
|
||||
createFrame(document, testId),
|
||||
mountFrame(document),
|
||||
writeTestDepsToDocument(frameReady),
|
||||
writeContentToFrame,
|
||||
buildProxyConsole(proxyConsole),
|
||||
writeContentToFrame
|
||||
initTestFrame(frameReady)
|
||||
);
|
||||
|
|
|
@ -10,9 +10,17 @@ class WorkerExecutor {
|
|||
this.getWorker = this.getWorker.bind(this);
|
||||
}
|
||||
|
||||
getWorker() {
|
||||
async getWorker() {
|
||||
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;
|
||||
|
@ -25,8 +33,8 @@ class WorkerExecutor {
|
|||
}
|
||||
}
|
||||
|
||||
execute(data, timeout = 1000) {
|
||||
const worker = this.getWorker();
|
||||
async execute(data, timeout = 1000) {
|
||||
const worker = await this.getWorker();
|
||||
return new Promise((resolve, reject) => {
|
||||
// Handle timeout
|
||||
const timeoutId = setTimeout(() => {
|
||||
|
|
|
@ -291,11 +291,9 @@ async function createTestRunnerForDOMChallenge(
|
|||
await context.setContent(build);
|
||||
await context.evaluate(
|
||||
async(sources, loadEnzyme) => {
|
||||
document.__source = sources && 'index' in sources ? sources['index'] : '';
|
||||
document.__getUserInput = fileName => sources[fileName];
|
||||
document.__frameReady = () => {};
|
||||
document.__loadEnzyme = loadEnzyme;
|
||||
await document.__initTestFrame();
|
||||
const code = sources && 'index' in sources ? sources['index'] : '';
|
||||
const getUserInput = fileName => sources[fileName];
|
||||
await document.__initTestFrame({ code, getUserInput, loadEnzyme });
|
||||
},
|
||||
sources,
|
||||
loadEnzyme
|
||||
|
|
Loading…
Reference in New Issue