From 1e1b755ce96ebf6ca3cd6d292761521dc0bd060a Mon Sep 17 00:00:00 2001 From: jhoffner Date: Tue, 3 May 2016 11:33:31 -0700 Subject: [PATCH] Bug fixes, split haskell into its own image, added support for language versions --- Makefile | 5 +- README.md | 80 ++++----- docker-compose.yml | 18 ++- docker/func.docker | 32 +--- docker/haskell.docker | 49 ++++++ docker/node.docker | 47 ++---- docker/python.docker | 2 +- lib/runners/javascript.js | 103 ++++++++---- lib/runners/python.js | 11 +- lib/runners/python3.js | 11 +- lib/runners/typescript.js | 24 ++- lib/shovel.js | 49 ++++-- package.json | 59 ++++--- run.js | 4 + test/runner.js | 3 +- test/runners/javascript_spec.js | 99 ++++++++++-- test/runners/python_spec.js | 277 ++++++++++++++++++-------------- test/runners/typescript_spec.js | 39 +++-- tsconfig.json | 6 + 19 files changed, 582 insertions(+), 336 deletions(-) create mode 100644 docker/haskell.docker create mode 100644 tsconfig.json diff --git a/Makefile b/Makefile index 2bc3b254..bf1f14f3 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ ${CONTAINERS}: base # Push docker containers to registry push_to_registry: docker push $(HOSTNAME)/base-runner - echo $(patsubst %, $(HOSTNAME)/%-runner, $(CONTAINERS)) | xargs -n 1 docker push + echo $(patsubst %, $(HOSTNAME)/%-runner, $(CONTAINERS)) | xargs -n 1 docker push # Remove docker processes that have exited cleanly docker_rm_exited: @@ -27,6 +27,7 @@ docker_rm_exited: # Cleanup temporary images that are no longer used docker_rmi_temporary: + docker rm $(docker ps -a) docker rmi $(docker images -q -f dangling=true) # Kill all of the in-flight and exited docker containers @@ -36,7 +37,7 @@ docker_rm: # Kill all docker images docker_rmi: docker_rm - [ ! -n "$(shell docker images -q)" ] || docker images -q | xargs -n 1 docker rmi -f + [ ! -n "$(shell docker images -q)" ] || docker images -q | xargs -n 1 docker rmi -f clean: docker_rm_exited docker_rmi_temporary diff --git a/README.md b/README.md index 8a1911c2..ee380666 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This project is used by [Codewars](http://www.codewars.com) and [Strive](https:/ Each time code is ran, it is executed within a Docker container in order to secure unsafe code execution. All execution is done within Docker, with a Node CLI app contained within each container -that manages the code execution and returns the result via stdout. +that manages the code execution for that specific language environment and returns the result via stdout. ## Contributions @@ -71,39 +71,42 @@ Many languages are currently supported in various states of completeness. This l **Legend:** `!!!` = Failing Specs, `???` = Status is unknown, `*` = Any -| Language | Basic Run | Test Integration | Codewars | Strive | Docker Image | Examples | Notes | -|----------------|--------------|------------------|--------------|----------------|----------------|--------------|-------------------------------------------------------------------------| -| Assembly (GAS) | !!! | | | | systems-runner | | Travis is failing, tests pass locally | -| Bash | ✓ | | Kumite Only | | * | | | -| C | !!! | !!! | | | systems-runner | | Travis is failing, tests pass locally | -| Clojure | ✓ | clojure.test | clojure.test | clojure.test | func-runner | clojure.test | | -| CoffeeScript | ✓ | cw-2 | cw-2 | cw-2 | node-runner | cw-2 | | -| C++ | ✓ | !!! | | | systems-runner | | Failing specs, systems image has also been temporarily removed | -| C# | ✓ | nunit | nunit | nunit | dotnet-runner | nunit | | -| Elixir | ✓ | | | | erlang-runner | | | -| Erlang | ✓ | | | | erlang-runner | | | -| F# | ✓ | | Kumite Only | | dotnet-runner | | | -| Go | ✓ | | Kumite Only | | alt-runner | | | -| Groovy | ✓ | | Kumite Only | | jvm-runner | | | -| Haskell | ✓ | hspec!!! | hspec | hspec | func-runner | hspec | An older version is running on CW & Strive that is fully functional | -| Java | ✓ | junit | Yes | Yes | jvm-runner | junit | | -| JavaScript | ✓ | cw-2, mocha | cw-2 | cw-2, mocha | node-runner | cw-2 | | -| Julia | ✓!!! | !!! | | | | | | -| Lisp | ✓ | | Kumite Only | | func-runner | | | -| Lua | ✓ | | Kumite Only | | alt-runner | | | -| ObjC | ??? | ??? | | | | | | -| OCAML | ✓ | | Kumite Only | | func-runner | | | -| Perl | ✓ | | Kumite Only | | * | | | -| Php | ✓ | | Kumite Only | | alt-runner | | | -| Python 2 | ✓ | cw-2, unittest | cw-2 | cw-2, unittest | python-runner | cw-2 | | -| Python 3 | ✓ | cw-2, unittest | | cw-2, unittest | python-runner | cw-2 | | -| R | ✓ | | | | alt-runner | | | -| Racket | ✓ | | Kumite Only | | func-runner | | | -| Ruby | ✓ | cw-2, rspec | cw-2 | cw-2, rspec | ruby-runner | cw-2 | | -| Rust | ✓ | | | | | | | -| Scala | ✓ | | Kumite Only | | jvm-runner | | | -| Swift | ??? | ??? | | | | | Current contribution designed for OSX, need to move to OS linux version | -| TypeScript | ✓ | mocha | Kumite Only | | node-runner | | TypeScript utilizes `require` instead of concatenating files | +| Language | Version | Basic Run | Test Integration | Codewars | Strive | Docker Image | Examples | Notes | +|----------------|---------------|--------------|------------------|--------------|----------------|----------------|--------------|-------------------------------------------------------------------------| +| Assembly (GAS) | | !!! | | | | systems-runner | | Travis is failing, tests pass locally | +| Bash | | ✓ | | Kumite Only | | * | | | +| C | | !!! | !!! | | | systems-runner | | Travis is failing, tests pass locally | +| Clojure | | ✓ | clojure.test | clojure.test | clojure.test | func-runner | clojure.test | | +| CoffeeScript | 1.10.0 | ✓ | cw-2 | cw-2 | cw-2 | node-runner | cw-2 | | +| C++ | | ✓ | !!! | | | systems-runner | | Failing specs, systems image has also been temporarily removed | +| C# | Mono 4.2.3 | ✓ | nunit | nunit | nunit | dotnet-runner | nunit | | +| Elixir | | ✓ | | | | erlang-runner | | | +| Erlang | | ✓ | | | | erlang-runner | | | +| F# | 4.0 | ✓ | | Kumite Only | | dotnet-runner | | | +| Go | | ✓ | | Kumite Only | | alt-runner | | | +| Groovy | | ✓ | | Kumite Only | | jvm-runner | | | +| Haskell | | ✓ | hspec!!! | hspec | hspec | haskell-runner | hspec | An older version is running on CW & Strive that is fully functional | +| Java | 1.8.0_91 | ✓ | junit | Yes | Yes | jvm-runner | junit | | +| JavaScript | 0.10.33 | ✓ | cw-2, mocha | cw-2 | cw-2, mocha | node-runner | cw-2 | | +| JavaScript | 0.10.33/Babel | ✓ | cw-2, mocha | cw-2 | cw-2, mocha | node-runner | cw-2 | | +| JavaScript | 6.0.0 | ✓ | cw-2, mocha | cw-2 | cw-2, mocha | node-runner | cw-2 | | +| JavaScript | 6.0.0/Babel | ✓ | cw-2, mocha | cw-2 | cw-2, mocha | node-runner | cw-2 | | +| Julia | | ✓!!! | !!! | | | | | | +| Lisp | | ✓ | | Kumite Only | | func-runner | | | +| Lua | | ✓ | | Kumite Only | | alt-runner | | | +| ObjC | | ??? | ??? | | | | | | +| OCAML | | ✓ | | Kumite Only | | func-runner | | | +| Perl | | ✓ | | Kumite Only | | * | | | +| Php | | ✓ | | Kumite Only | | alt-runner | | | +| Python | 2.7.6 | ✓ | cw-2, unittest | cw-2 | cw-2, unittest | python-runner | cw-2 | | +| Python | 3.4.3 | ✓ | cw-2, unittest | | cw-2, unittest | python-runner | cw-2 | | +| R | | ✓ | | | | alt-runner | | | +| Racket | | ✓ | | Kumite Only | | func-runner | | | +| Ruby | 2.3.0 | ✓ | cw-2, rspec | cw-2 | cw-2, rspec | ruby-runner | cw-2 | | +| Rust | | ✓ | | | | | | | +| Scala | | ✓ | | Kumite Only | | jvm-runner | | | +| Swift | | ??? | ??? | | | | | Current contribution designed for OSX, need to move to OS linux version | +| TypeScript | 1.8.10 | ✓ | mocha | Kumite Only | | node-runner | | TypeScript utilizes `require` instead of concatenating files | ## Setup @@ -111,7 +114,7 @@ You should have Docker installed, if not do that first. Before you can run any o environments you will need to build the proper Docker image. To get started lets work with the node image. -Run `make base node` to build the base and node images. This will take a few minutes. +Run `make node` to build the base and node images. This will take a few minutes. Once you image is built, you can create a container to work within it. Doing this means you do not have to worry about having any of the project dependencies loaded directly on your machine. @@ -199,7 +202,7 @@ to create a rich and even interactive test result output. ## How to add a new language -> Note: These steps will assuming adding a completely new language to the project. Many languages are currently in an incomplete +> Note: These steps all assume you are adding a completely new language to the project. Many languages are currently in an incomplete state so not all steps may be needed in your case 1. Install the language and its related packages on one of the Docker images. We have grouped many of the Docker images @@ -232,9 +235,8 @@ Each of these strategies is passed in a `run` method which is used to ultimately There is currently no way of handling language/package versioning well. This is largely caused by the Node CLI tool having to be baked in to the Docker container. A more ideal solution would involve keeping the CLI outside of Docker (or within its own sibling container) and communicating to language specific containers. This would allow us to easily swap out container versions that have no -dependencies on the CLI codebase. However this would involve having to create a shared volume so that files can be passed in -to each language container, which has security implications. Upgrading to this newer paradigm and resolving any security concerns -is the next big phase of this project. Remember - this project is ran on production servers running user submitted code. +dependencies on the CLI codebase. This would involve having to utilize the `docker cp` command to copy +files to the docker container in order to be compiled. Upgrading to this newer paradigm is the next big phase of this project. ### Ability to send input parameters and return data diff --git a/docker-compose.yml b/docker-compose.yml index 5580121b..380b5ea2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: - ./examples:/runner/examples - ./frameworks:/runner/frameworks - ./test:/runner/test + - ./tsconfig.json:/runner/tsconfig.json entrypoint: '' command: bash @@ -50,6 +51,16 @@ services: entrypoint: '' command: bash + haskell-runner: + image: codewars/haskell-runner + volumes: + - ./lib:/runner/lib + - ./examples:/runner/examples + - ./frameworks:/runner/frameworks + - ./test:/runner/test + entrypoint: '' + command: bash + systems-runner: image: codewars/systems-runner volumes: @@ -81,7 +92,7 @@ services: command: bash dotnet-runner: - image: codewars/alt-runner + image: codewars/dotnet-runner volumes: - ./lib:/runner/lib - ./examples:/runner/examples @@ -134,6 +145,7 @@ services: - ./examples:/runner/examples - ./frameworks:/runner/frameworks - ./test:/runner/test + - ./tsconfig.json:/runner/tsconfig.json entrypoint: 'node run -l typescript' typescript_test: @@ -200,7 +212,7 @@ services: entrypoint: 'mocha -t 5000 test/runners/ruby_spec.js' haskell: - image: codewars/func-runner + image: codewars/haskell-runner volumes: - ./lib:/runner/lib - ./examples:/runner/examples @@ -209,7 +221,7 @@ services: entrypoint: 'node run -l haskell' haskell_test: - image: codewars/func-runner + image: codewars/haskell-runner volumes: - ./lib:/runner/lib - ./examples:/runner/examples diff --git a/docker/func.docker b/docker/func.docker index 37d2ad3b..515b3b2c 100644 --- a/docker/func.docker +++ b/docker/func.docker @@ -14,36 +14,6 @@ RUN apt-get -y install sbcl # Install OCAML RUN apt-get -y install ocaml-nox -# Needed to run add-apt-repository -RUN apt-get -y install software-properties-common - -# Install Haskell -## ensure locale is set during build -ENV LANG C.UTF-8 - -RUN apt-get -y install libghc-zlib-dev happy - -RUN echo 'deb http://ppa.launchpad.net/hvr/ghc/ubuntu trusty main' > /etc/apt/sources.list.d/ghc.list && \ - echo 'deb http://download.fpcomplete.com/debian/jessie stable main'| tee /etc/apt/sources.list.d/fpco.list && \ - # hvr keys - apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F6F88286 && \ - # fpco keys - apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C5705533DA4F78D8664B5DC0575159689BEFB442 && \ - apt-get update && \ - apt-get install -y --no-install-recommends cabal-install-1.22 ghc-7.10.3 happy-1.19.5 alex-3.1.4 \ - stack zlib1g-dev libtinfo-dev libsqlite3-0 libsqlite3-dev ca-certificates g++ && \ - rm -rf /var/lib/apt/lists/* - -ENV PATH /root/.cabal/bin:/root/.local/bin:/opt/cabal/1.22/bin:/opt/ghc/7.10.3/bin:/opt/happy/1.19.5/bin:/opt/alex/3.1.4/bin:$PATH - -# Install Haskell Packages -RUN apt-get -y install libghc-zlib-dev pkg-config happy -RUN cabal update -RUN cabal install hspec -RUN cabal install haskell-src-exts lens -RUN cabal install esqueleto persistent-sqlite persistent-template -RUN cabal install split ifelse - # ADD cli-runner and install node deps ADD . /runner @@ -56,7 +26,7 @@ USER codewarrior # Set environment variables ENV USER codewarrior ENV HOME /home/codewarrior -#RUN mocha -t 5000 test/runners/{haskell,ocaml,lisp,racket}_spec.js +RUN mocha -t 5000 test/runners/{ocaml,racket}_spec.js #timeout is a fallback in case an error with node #prevents it from exiting properly diff --git a/docker/haskell.docker b/docker/haskell.docker new file mode 100644 index 00000000..51ede04a --- /dev/null +++ b/docker/haskell.docker @@ -0,0 +1,49 @@ +# BUILD-USING: docker build -t codewars/runner-func . +# TEST-USING: docker run --rm -i -t --name=test-runner-func --entrypoint=/bin/bash codewars/runner-haskell -s +# RUN-USING: docker run --rm --name=runner-func codewars/runner-func --help + +# Pull base image. +FROM codewars/base-runner + +# Install Haskell +## ensure locale is set during build +ENV LANG C.UTF-8 + +RUN apt-get -y install libghc-zlib-dev happy + +# Needed to run add-apt-repository +RUN apt-get -y install software-properties-common + +# Install Haskell +RUN add-apt-repository ppa:hvr/ghc +RUN apt-get update +RUN apt-get install -y ghc-7.6.3 cabal-install +RUN su codewarrior -c "cd ; cabal update" +RUN su codewarrior -c "cd ; cabal install cabal" +RUN echo 'remote-repo: stackage:http://www.stackage.org/lts-1.15' >> /home/codewarrior/.cabal/config +RUN su codewarrior -c "cd ; cabal update" +RUN su codewarrior -c "cd ; cabal install cabal" + +# Install Haskell Packages +RUN apt-get -y install libghc-zlib-dev pkg-config happy +RUN su codewarrior -c "cd ; cabal install split ifelse" +RUN su codewarrior -c "cd ; cabal install esqueleto persistent-sqlite persistent-template" +RUN su codewarrior -c "cd ; cabal install haskell-src-exts lens" +RUN su codewarrior -c "cd ; cabal install hspec-2.1.0 hspec-core-2.1.0 hspec-discover-2.1.0" +# ADD cli-runner and install node deps +ADD . /runner + +WORKDIR /runner +RUN npm install --production + +# Run the test suite to make sure this thing works + +USER codewarrior +# Set environment variables +ENV USER codewarrior +ENV HOME /home/codewarrior +RUN mocha -t 5000 test/runners/haskell_spec.js + +#timeout is a fallback in case an error with node +#prevents it from exiting properly +ENTRYPOINT ["timeout", "15", "node"] diff --git a/docker/node.docker b/docker/node.docker index 2b3d7752..9a79e9dd 100644 --- a/docker/node.docker +++ b/docker/node.docker @@ -4,33 +4,10 @@ # Pull base image. FROM codewars/base-runner -# Install additional libraries -# HACK: installing globallying and then linking has a less buggy experience. -# NOTE: we dont configure these via package.json in order to prevent having to re-install them on each code change -RUN npm link chai -RUN npm install immutable -gq; npm link immutable -RUN npm install deku -gq; npm link deku -RUN npm install quickcheck -gq; npm link quickcheck -RUN npm install should -gq; npm link should -RUN npm install expect -gq; npm link expect -RUN npm install chai-spies -gq; npm link chai-spies -RUN npm install chai-stats -gq; npm link chai-stats -RUN npm install chai-factories -gq; npm link chai-factories -RUN npm install chai-things -gq; npm link chai-things -RUN npm install chai-fuzzy -gq; npm link chai-fuzzy -RUN npm install chai-interface -gq; npm link chai-interface -RUN npm install chai-change -gq; npm link chai-change -RUN npm install chai-subset -gq; npm link chai-subset -RUN npm install rx -gq; npm link rx -RUN npm install baconjs -gq; npm link baconjs -RUN npm install lodash -gq; npm link lodash -RUN npm install react -gq; npm link react -RUN npm install react-dom -gq; npm link react-dom -RUN npm install mongoose -gq; npm link mongoose -RUN npm install mongodb -gq; npm link mongodb -RUN npm install redis -gq; npm link redis -RUN npm install sqlite3 -gq; npm link sqlite3 -RUN npm install elasticsearch -gq; npm link elasticsearch +# Install Node manager so that we can have multiple versions of node +RUN npm -g install n +RUN n 0.10.33 +RUN n 6.0.0 # Install Coffeescript RUN npm -g install coffee-script @@ -38,8 +15,18 @@ RUN npm -g install coffee-script # Install TypeScript RUN npm -g install typescript +COPY package.json package.json +RUN npm install +RUN npm install --only=dev + + +# Install additional libraries +# NOTE: due to this issue https://github.com/npm/npm/issues/9863 we need to install everything at once, which sucks +# because we lose the ability to quickly add new ones as a new layer +# NOTE: we dont configure these via package.json in order to prevent having to re-install them on each code change + # install TypeScript type definitions -RUN npm install -gq typings; npm link typings +RUN npm install -gq typings RUN typings install node --ambient RUN typings install mocha --ambient @@ -59,7 +46,7 @@ WORKDIR /runner RUN npm install RUN npm install --only=dev RUN npm dedupe # needed to fix slow babeljs performance -ENV NODE_PATH /usr/lib/node_modules +ENV NODE_PATH /usr/lib/node_modules:/runner/node_modules # Set environment variables USER codewarrior @@ -67,7 +54,7 @@ ENV USER codewarrior ENV HOME /home/codewarrior # Run the test suite to make sure this thing works -#RUN mocha -t 5000 test/runners/{javascript,coffeescript,typescript}_spec.js +RUN mocha -t 5000 test/runners/{javascript,coffeescript,typescript}_spec.js #timeout is a fallback in case an error with node #prevents it from exiting properly diff --git a/docker/python.docker b/docker/python.docker index db7a0fe4..2f83fb8d 100644 --- a/docker/python.docker +++ b/docker/python.docker @@ -31,7 +31,7 @@ USER codewarrior ENV USER codewarrior ENV HOME /home/codewarrior -RUN mocha -t 5000 test/runners/{python,python3}_spec.js +RUN mocha -t 5000 test/runners/python_spec.js #timeout is a fallback in case an error with node #prevents it from exiting properly diff --git a/lib/runners/javascript.js b/lib/runners/javascript.js index 14063cac..d56cfd55 100644 --- a/lib/runners/javascript.js +++ b/lib/runners/javascript.js @@ -14,7 +14,7 @@ module.exports.run = function run(opts, cb) if (opts.setup) code = opts.setup + ';\n' + code; - run({name: 'node', 'args': ['-e', transform(code)]}); + runNode(opts, code, run); }, testIntegration: function (run) { @@ -71,6 +71,7 @@ function prepareMocha(opts, interfaceType, run) { code += config.snippets.javascript.closure.end; var solutionFile = util.codeWriteSync('javascript', transform(code), dir, 'mocha', true); + // NOTE: Mocha based testing currently does not support Node versioning run({name: 'mocha', 'args': ['--harmony', '-u', interfaceType, '-R', 'mocha-reporter', solutionFile]}); } @@ -94,39 +95,79 @@ function prepareCw2(opts, run) code += opts.fixture; code += config.snippets.javascript.inlineTestFixture.end; code += config.snippets.javascript.end; - run({name: 'node', 'args': ['-e', transform(code)]}); + + runNode(opts, code, run); +} + +function runNode(opts, code, run) { + var version = (opts.languageVersion || '6.x').split('/')[0] + + // support 6.x/babel + if (opts.languageVersion && opts.languageVersion.split('/')[1] == 'babel') { + code = transform(code); + } + run({name: `/usr/local/n/versions/node/${nodeVersion(version)}/bin/node`, 'args': ['-e', code]}); } -function transform(code) { +function nodeVersion(version) { + switch(version) { + case "6.x": + return "6.0.0"; + case "0.10.x": + return "0.10.33" + default: + return version; + } +} + +function transform(code, version) { try { - return require('babel-core').transform(code, { - presets: ["stage-1", "react"], - plugins: [ - "transform-runtime", - "check-es2015-constants", - "transform-es2015-arrow-functions", - "transform-es2015-block-scoped-functions", - "transform-es2015-block-scoping", - "transform-es2015-classes", - "transform-es2015-computed-properties", - "transform-es2015-destructuring", - "transform-es2015-duplicate-keys", - "transform-es2015-for-of", - "transform-es2015-function-name", - "transform-es2015-literals", - "transform-es2015-object-super", - "transform-es2015-parameters", - "transform-es2015-shorthand-properties", - "transform-es2015-spread", - "transform-es2015-sticky-regex", - "transform-es2015-template-literals", - "transform-es2015-typeof-symbol", - "transform-es2015-unicode-regex", - "transform-regenerator", - ], - ast: false, - filename: 'kata' - }).code; + if (version == '0.10.x') { + return require('babel-core').transform(code, { + presets: ["stage-1", "react"], + plugins: [ + "transform-runtime", + "check-es2015-constants", + "angular2-annotations", + "transform-decorators-legacy", + "transform-class-properties", + "transform-flow-strip-types", + "transform-es2015-arrow-functions", + "transform-es2015-block-scoped-functions", + "transform-es2015-block-scoping", + "transform-es2015-classes", + "transform-es2015-computed-properties", + "transform-es2015-destructuring", + "transform-es2015-duplicate-keys", + "transform-es2015-for-of", + "transform-es2015-function-name", + "transform-es2015-literals", + "transform-es2015-object-super", + "transform-es2015-parameters", + "transform-es2015-shorthand-properties", + "transform-es2015-spread", + "transform-es2015-sticky-regex", + "transform-es2015-template-literals", + "transform-es2015-typeof-symbol", + "transform-es2015-unicode-regex", + "transform-regenerator", + ], + ast: false, + filename: 'kata' + }).code; + } else { + return require('babel-core').transform(code, { + presets: ["stage-1", "node5", "react"], + plugins: [ + "angular2-annotations", + "transform-decorators-legacy", + "transform-class-properties", + "transform-flow-strip-types", + ], + ast: false, + filename: 'kata' + }).code; + } } catch(ex) { var msg = ex.message; diff --git a/lib/runners/python.js b/lib/runners/python.js index bac2d9b6..3abd5fc7 100644 --- a/lib/runners/python.js +++ b/lib/runners/python.js @@ -5,7 +5,7 @@ module.exports.run = function run(opts, cb) { shovel.start(opts, cb, { solutionOnly: function (run) { var code = [opts.setup, opts.solution].join("\n"); - run({name: 'python', 'args': ['-c', code]}); + runVersion(opts.languageVersion, code, run); }, testIntegration: function (run) { var code; @@ -30,7 +30,7 @@ module.exports.run = function run(opts, cb) { throw new Error('test framework is not supported'); } } - run({name: 'python', 'args': ['-c', code]}); + runVersion(opts.languageVersion, code, run); }, sanitizeStdErr: function (err) { // get rid of some of the noisy content. We remove line numbers since @@ -41,3 +41,10 @@ module.exports.run = function run(opts, cb) { } }); }; + +function runVersion(version, code, run) { + var name = "python"; + if (version && version[0] == '3') name = "python3"; + + run({name: name, 'args': ['-c', code]}); +} diff --git a/lib/runners/python3.js b/lib/runners/python3.js index e51bd510..d0e6c6aa 100644 --- a/lib/runners/python3.js +++ b/lib/runners/python3.js @@ -1,4 +1,9 @@ -var path = require('path'), - pythonRunnerCode = require('fs').readFileSync(path.join(__dirname,'python.js'),'utf8'); +// this file is just here for backwards compatibility since python 3 used to be considered its own language. +var python = require('./python'); + +module.exports.run = function run(opts, cb) { + opts.languageVersion = '3'; // we only care about the major version + python.run(opts, cb); +} + -eval(pythonRunnerCode.split('python').join('python3')); diff --git a/lib/runners/typescript.js b/lib/runners/typescript.js index bfdc6545..f4cbc420 100644 --- a/lib/runners/typescript.js +++ b/lib/runners/typescript.js @@ -41,21 +41,29 @@ module.exports.run = function run(opts, cb) { function prepareMocha(opts, interfaceType, run) { var dir = temp.mkdirSync('typescript'); - var code = ''; - - if (opts.setup) code = opts.setup + ';\n'; - - code += opts.solution + ';\n'; + var code = ` + /// + /// + ${opts.setup} + ${opts.solution} + ` var codeFile = util.codeWriteSync('typescript', code, dir, 'solution.ts', true); util.exec('tsc --module commonjs ' + codeFile, function () { - var spec = "var solution = require('./solution.js')\n" + opts.fixture, - specFile = util.codeWriteSync('typescript', spec, dir, 'spec.ts', true); + var spec = ` + /// + /// + /// + import solution = require('./solution') + ${opts.fixture} + `; + + var specFile = util.codeWriteSync('typescript', spec, dir, 'spec.ts', true); util.exec('tsc --module commonjs ' + specFile, function () { specFile = specFile.replace('.ts', '.js') - run({name: 'mocha', 'args': ['--harmony', '-u', interfaceType, '-R', 'mocha-reporter', specFile]}); + run({name: 'mocha', 'args': ['-u', interfaceType, '-R', 'mocha-reporter', specFile]}); }); }); } diff --git a/lib/shovel.js b/lib/shovel.js index 69255dfa..4c654868 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -1,7 +1,8 @@ var util = require('util'), spawn = require('child_process').spawn, config = require('./config'), - os = require('os'); + os = require('os'), + utf8 = require('utf8'); // used for indicating an error which is related to user code and not an application error. // useful for when we run code compilation inside of this host process. @@ -66,10 +67,10 @@ function run(opts, strategies, cb) } } -function exec(opts, name, args, process_options, process_stdin, cb) +function exec(opts, name, args, processOptions, processStdin, cb) { var args = args || [], - child = spawn(name, args, process_options), + child = spawn(name, args, processOptions), buffer = {stdout: [], stderr: []}, start = new Date(), finished = false, @@ -100,18 +101,19 @@ function exec(opts, name, args, process_options, process_stdin, cb) } } - if (process_stdin) child.stdin.write(process_stdin); + if (processStdin) child.stdin.write(processStdin); // Listen child.stdout.on('data', function (data) { if (!!data) { + //console.log(data.toString()); buffer.stdout.push(data.toString()); stdoutLength += data.length; } - if (stdoutLength > 750000) + if (stdoutLength > 1000000) { buffer.status = 'max_buffer_reached'; exit('Max Buffer reached: Too much information has been written to stdout.'); @@ -186,15 +188,42 @@ function reportBuffer(opts, buffer, strategies) if (opts.format == 'json') { - console.log(JSON.stringify(buffer)); + var json = JSON.stringify(buffer); + writeToStream(process.stdout, json, "\\n"); } else { - if (buffer.stdout) console.log(buffer.stdout); - if (buffer.stderr) console.error(buffer.stderr); - if (buffer.wallTime && opts.debug) - { + if (buffer.stdout) writeToStream(process.stdout, buffer.stdout, "\n") + if (buffer.stderr) writeToStream(process.stderr, buffer.stderr, "\n") + if (buffer.wallTime && opts.debug) { console.info(buffer.wallTime + 'ms'); } } } + +// we need to chunk the data back out to handle strange inconsistency issues with large data. +// Ideally we could chunk based off of character count but for some reason chunking by line breaks +// is the only thing that is consistent. +function writeToStream(stream, data, linebreak) { + data.split(linebreak).forEach((line, i, arr) => { + // don't write a line break on the last line + return stream.write(utf8.encode(line) + (i != arr.length - 1 ? linebreak : '')) + }); +} + +//function writeToStream(stream, data, offset) { +// var len = 1000, size = Math.ceil(data.length/len), offset = offset || 0; +// +// for (var i = offset; i < size; i++) { +// offset = i * len; +// chunk = data.substring(offset, offset + len); +// stream.write(chunk) +// // stream.write always returns true yet it doesnt always output everything correctly. +// if (false) { +// stream.once('drain', function() { +// //writeToStream(stream, data, offset); +// }); +// return; +// } +// } +//} diff --git a/package.json b/package.json index a1b1db96..e63e8cd1 100644 --- a/package.json +++ b/package.json @@ -23,36 +23,43 @@ "mocha": "1.19.x", "nomnom": "1.6.x", "temp": "*", - "asyncawait": "*", "bluebird": "*", "js-yaml": "*", + "utf8": "^2.1.1", "mocha-reporter": "git://github.com/codewars/mocha-reporter" }, + "devDependencies": { - "babel-core": "6.7.x", - "babel-runtime": "6.6.x", - "babel-preset-stage-1": "6.5.x", - "babel-preset-react": "6.5.x", - "babel-plugin-transform-runtime": "6.7.*", - "babel-plugin-check-es2015-constants": "*", - "babel-plugin-transform-es2015-arrow-functions": "*", - "babel-plugin-transform-es2015-block-scoped-functions": "*", - "babel-plugin-transform-es2015-block-scoping": "*", - "babel-plugin-transform-es2015-classes": "*", - "babel-plugin-transform-es2015-computed-properties": "*", - "babel-plugin-transform-es2015-destructuring": "*", - "babel-plugin-transform-es2015-duplicate-keys": "*", - "babel-plugin-transform-es2015-for-of": "*", - "babel-plugin-transform-es2015-function-name": "*", - "babel-plugin-transform-es2015-literals": "*", - "babel-plugin-transform-es2015-object-super": "*", - "babel-plugin-transform-es2015-parameters": "*", - "babel-plugin-transform-es2015-shorthand-properties": "*", - "babel-plugin-transform-es2015-spread": "*", - "babel-plugin-transform-es2015-sticky-regex": "*", - "babel-plugin-transform-es2015-template-literals": "*", - "babel-plugin-transform-es2015-typeof-symbol": "*", - "babel-plugin-transform-es2015-unicode-regex": "*", - "babel-plugin-transform-regenerator": "*" + "babel-core": "*", + "babel-preset-node5": "*", + "babel-preset-react": "*", + "babel-preset-stage-0": "*", + "babel-preset-stage-1": "*", + "babel-plugin-angular2-annotations": "5.0.0", + "babel-plugin-transform-class-properties": "6.6.0", + "babel-plugin-transform-decorators-legacy": "1.3.4", + "babel-plugin-transform-flow-strip-types": "6.7.0", + "babel-runtime": "*", + "baconjs": "^0.7.84", + "chai-change": "^1.0.0", + "chai-factories": "^0.1.0", + "chai-spies": "^0.7.1", + "chai-stats": "^0.3.0", + "chai-subset": "^1.2.2", + "deku": "^2.0.0-rc16", + "elasticsearch": "^11.0.1", + "expect": "^1.18.0", + "immutable": "^3.8.1", + "lodash": "^2.4.2", + "mongodb": "^2.1.18", + "mongoose": "^4.4.14", + "quickcheck": "0.0.4", + "react": "^15.0.2", + "react-dom": "^15.0.2", + "redis": "^2.6.0-2", + "rx": "^4.1.0", + "should": "^8.3.1", + "typings": "^0.8.1", + "utf8": "^2.1.1" } } diff --git a/run.js b/run.js index 5674e5e4..0f957247 100644 --- a/run.js +++ b/run.js @@ -17,6 +17,10 @@ var run = require('./lib/runner').run, abbr: 'l', help: 'The language to execute the code in' }, + languageVersion: { + abbr: 'lv', + help: 'The language version that should be used' + }, testFramework: { abbr: 't', full: 'test-framework', diff --git a/test/runner.js b/test/runner.js index 708673c8..acfb3110 100644 --- a/test/runner.js +++ b/test/runner.js @@ -17,7 +17,7 @@ function iterateCodeExamples(language, cb) { } } -module.exports.assertCodeExamples = function(language) { +module.exports.assertCodeExamples = function(language, version) { describe('example challenges', function() { iterateCodeExamples(language, function (framework, name, example) { it ('should define an initial code block', function() { @@ -27,6 +27,7 @@ module.exports.assertCodeExamples = function(language) { it ('should have a passing ' + name + ' example', function(done) { runner.run({ language: language, + languageVersion: version, setup: example.setup, code: example.answer, fixture: example.fixture, diff --git a/test/runners/javascript_spec.js b/test/runners/javascript_spec.js index 79d34e20..3ac62651 100644 --- a/test/runners/javascript_spec.js +++ b/test/runners/javascript_spec.js @@ -11,6 +11,27 @@ describe( 'javascript runner', function(){ done(); }); }); + it( 'should handle JSON.stringify ', function(done){ + runner.run({language: 'javascript', code: 'console.log(JSON.stringify({a: 1}))'}, function(buffer) { + console.log(buffer.stderr); + expect(buffer.stdout).to.contain('{"a":1}'); + done(); + }); + }); + + it( 'should be able to handle large output data', function(done) { + runner.run({ + language: 'javascript', + code: ` + for(i = 0;i < 9999; i++){ + console.log(i * 10); + } + ` + }, function(buffer) { + expect(buffer.stderr).to.equal(''); + done(); + }); + }); it( 'should handle es6 code evaluation', function(done){ runner.run({language: 'javascript', code: 'let a = 42; console.log(42);'}, function(buffer) { expect(buffer.stdout).to.equal('42\n'); @@ -18,14 +39,42 @@ describe( 'javascript runner', function(){ }); }); - it( 'should handle bad syntax', function(done){ - runner.run({language: 'javascript', code: 'var a = function(){returns 42;};\na();'}, function(buffer) { + it( 'should handle bad babel syntax', function(done){ + runner.run({language: 'javascript', languageVersion: '6.x/babel', code: 'var a = function(){returns 42;};\na();'}, function(buffer) { expect(buffer.stderr).to.contain('kata: Unexpected token:27'); done(); }); }); + it( 'should handle react syntax', function(done){ - runner.run({language: 'javascript', code: 'var React = require("react");var ReactDOM = require("react-dom/server");var div =

Test

;console.log(ReactDOM.renderToStaticMarkup(div));'}, function(buffer) { + runner.run({ + language: 'javascript', + languageVersion: '6.0.0/babel', + code: ` + var React = require("react"); + var ReactDOM = require("react-dom/server"); + var div =

Test

; + console.log(ReactDOM.renderToStaticMarkup(div)); + ` + }, + function(buffer) { + expect(buffer.stdout).to.contain('

Test

'); + done(); + }); + }); + + it( 'should handle react syntax using 0.10.x', function(done){ + runner.run({ + language: 'javascript', + languageVersion: '0.10.x/babel', + code: ` + var React = require("react"); + var ReactDOM = require("react-dom/server"); + var div =

Test

; + console.log(ReactDOM.renderToStaticMarkup(div)); + ` + }, + function(buffer) { expect(buffer.stdout).to.contain('

Test

'); done(); }); @@ -65,7 +114,7 @@ describe( 'javascript runner', function(){ it( 'should handle failures', function(done){ runner.run({ language: 'javascript', - code: 'a = {b: 2};', + code: 'var a = {b: 2};', fixture: 'var assert = require("chai").assert;describe("test", function(){describe("failures", function(){it("should be 1", function(){assert.equal(1, a.b);})})});', testFramework: 'mocha_bdd'}, function(buffer) { @@ -76,7 +125,7 @@ describe( 'javascript runner', function(){ it( 'should handle errors', function(done){ runner.run({ language: 'javascript', - code: 'a = {b: 2};', + code: 'var a = {b: 2};', fixture: 'describe("test", function(){describe("failures", function(){it("should be 1", function(){throw new Error("test error");})})});', testFramework: 'mocha_bdd'}, function(buffer) { @@ -101,7 +150,7 @@ describe( 'javascript runner', function(){ it( 'should handle failures', function(done){ runner.run({ language: 'javascript', - code: 'a = {b: 2};', + code: 'var a = {b: 2};', fixture: 'var assert = require("assert"); suite("test", function(){suite("failures", function(){test("should be 1", function(){assert.equal(1, a.b);})})});', testFramework: 'mocha_tdd'}, function(buffer) { @@ -109,10 +158,21 @@ describe( 'javascript runner', function(){ done(); }); }); + it( 'should handle chai failures', function(done){ + runner.run({ + language: 'javascript', + code: 'var a = {b: 2};', + fixture: 'var assert = require("chai").assert; suite("test", function(){suite("failures", function(){test("should be 1", function(){assert.equal(1, a.b);})})});', + testFramework: 'mocha_tdd'}, + function(buffer) { + expect(buffer.stdout).to.contain(''); + done(); + }); + }); it( 'should handle errors', function(done){ runner.run({ language: 'javascript', - code: 'a = {b: 2};', + code: 'var a = {b: 2};', fixture: 'suite("test", function(){suite("failures", function(){test("should be 1", function(){throw new Error("test error");})})});', testFramework: 'mocha_tdd'}, function(buffer) { @@ -124,7 +184,26 @@ describe( 'javascript runner', function(){ describe('cw-2', function() { it( 'should handle outputting objects', function(done){ - runner.run({language: 'javascript', code: 'a = {b: 2};', fixture: 'Test.expect(false, a);', testFramework: 'cw-2'}, function(buffer) { + runner.run({ + language: 'javascript', + code: 'var a = {b: 2};', + fixture: 'Test.expect(false, a);', + testFramework: 'cw-2' + }, function(buffer) { + expect(buffer.stdout).to.contain('{ b: 2 }'); + expect(buffer.stdout).to.contain(''); + done(); + }); + }); + it( 'should handle outputting objects with 0.10.33', function(done){ + // only 0.10.33 allows us to declare a without var + runner.run({ + language: 'javascript', + languageVersion: '0.10.33', + code: 'a = {b: 2};', + fixture: 'Test.expect(false, a);', + testFramework: 'cw-2' + }, function(buffer) { expect(buffer.stdout).to.contain('{ b: 2 }'); expect(buffer.stdout).to.contain(''); done(); @@ -132,7 +211,7 @@ describe( 'javascript runner', function(){ }); it('should handle a basic assertion', function(done){ - runner.run({language: 'javascript', code: 'a = 1', fixture: 'Test.expect(a == 1);', testFramework: 'cw-2'}, function(buffer) { + runner.run({language: 'javascript', code: 'var a = 1', fixture: 'Test.expect(a == 1);', testFramework: 'cw-2'}, function(buffer) { expect(buffer.stdout).to.equal('Test Passed\n'); done(); }); @@ -146,7 +225,7 @@ describe( 'javascript runner', function(){ }); it('should handle a basic failed test', function(done){ - runner.run({language: 'javascript', code: 'a = 1', fixture: 'Test.expect(a == 2)', testFramework: 'cw-2'}, function(buffer) { + runner.run({language: 'javascript', code: 'var a = 1', fixture: 'Test.expect(a == 2)', testFramework: 'cw-2'}, function(buffer) { expect(buffer.stdout).to.equal('Value is not what was expected\n'); done(); }); diff --git a/test/runners/python_spec.js b/test/runners/python_spec.js index f4901f6d..34f8af1a 100644 --- a/test/runners/python_spec.js +++ b/test/runners/python_spec.js @@ -2,147 +2,174 @@ var expect = require('chai').expect; var runner = require('../runner'); describe('python runner', function () { + // These specs are compatible with both Python 2 and 3 + ['2', '3'].forEach(lv => { + describe('.run', function () { + runner.assertCodeExamples('python', lv); - describe('.run', function () { - runner.assertCodeExamples('python'); - - it('should handle basic code evaluation', function (done) { - runner.run({language: 'python', code: 'import sys; sys.stdout.write("42")'}, function (buffer) { - console.log(buffer); - expect(buffer.stdout).to.equal('42'); - done(); - }); - }); - it('stderr', function (done) { - runner.run({language: 'python', code: 'import sys; sys.stderr.write("Error! Codewars cannot and will not accept any more Fibonacci kata.")'}, function (buffer) { - console.log(buffer); - expect(buffer.stderr).to.equal("Error! Codewars cannot and will not accept any more Fibonacci kata."); - done(); - }); - }); - it('stderr', function (done) { - runner.run({language: 'python', code: 'import sys; sys.stderr.write("florp"); sys.stdout.write("foop")'}, function (buffer) { - console.log(buffer); - expect(buffer.stderr).to.equal("florp"); - expect(buffer.stdout).to.equal("foop"); - done(); - }); - }); - }); - describe('cw-2', function () { - it('should handle a basic assertion', function (done) { - runner.run({ + it('should handle basic code evaluation', function (done) { + runner.run({ language: 'python', - code: 'a = 1', - fixture: 'test.expect(a == 1)', - testFramework: 'cw-2' - }, - function (buffer) { + languageVersion: lv, + code: 'import sys; sys.stdout.write("42")' + }, function (buffer) { console.log(buffer); - expect(buffer.stdout).to.equal('Test Passed\n'); + expect(buffer.stdout).to.equal('42'); done(); }); - }); - it('should handle a basic assert_equals', function (done) { - runner.run({ - language: 'python', - code: 'a = 1', - fixture: 'test.assert_equals(a, 1)', - testFramework: 'cw-2' - }, - function (buffer) { - console.log(buffer); - expect(buffer.stdout).to.equal('Test Passed\n'); - done(); - }); - }); - it('should handle a basic setup', function (done) { - runner.run({ + }); + it('stderr', function (done) { + runner.run({ language: 'python', - code: 'a = 1', - setup: 'b = 2', - fixture: 'test.assert_equals(b, 2)', - testFramework: 'cw-2' - }, - function (buffer) { + languageVersion: lv, + code: 'import sys; sys.stderr.write("Error! Codewars cannot and will not accept any more Fibonacci kata.")' + }, function (buffer) { console.log(buffer); - expect(buffer.stdout).to.equal('Test Passed\n'); + expect(buffer.stderr).to.equal("Error! Codewars cannot and will not accept any more Fibonacci kata."); done(); }); - }); - it('should handle a failed assertion', function (done) { - runner.run({ + }); + it('stderr', function (done) { + runner.run({ language: 'python', - code: 'a = 1', - fixture: 'test.expect(a == 2)', - testFramework: 'cw-2' - }, - function (buffer) { + languageVersion: lv, + code: 'import sys; sys.stderr.write("florp"); sys.stdout.write("foop")' + }, function (buffer) { console.log(buffer); - expect(buffer.stdout).to.equal('Value is not what was expected\n'); + expect(buffer.stderr).to.equal("florp"); + expect(buffer.stdout).to.equal("foop"); done(); }); + }); }); + describe('cw-2', function () { + it('should handle a basic assertion', function (done) { + runner.run({ + language: 'python', + languageVersion: lv, + code: 'a = 1', + fixture: 'test.expect(a == 1)', + testFramework: 'cw-2' + }, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Test Passed\n'); + done(); + }); + }); + it('should handle a basic assert_equals', function (done) { + runner.run({ + language: 'python', + languageVersion: lv, + code: 'a = 1', + fixture: 'test.assert_equals(a, 1)', + testFramework: 'cw-2' + }, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Test Passed\n'); + done(); + }); + }); + it('should handle a basic setup', function (done) { + runner.run({ + language: 'python', + languageVersion: lv, + code: 'a = 1', + setup: 'b = 2', + fixture: 'test.assert_equals(b, 2)', + testFramework: 'cw-2' + }, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Test Passed\n'); + done(); + }); + }); + it('should handle a failed assertion', function (done) { + runner.run({ + language: 'python', + languageVersion: lv, + code: 'a = 1', + fixture: 'test.expect(a == 2)', + testFramework: 'cw-2' + }, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Value is not what was expected\n'); + done(); + }); + }); - it('should handle a failed assertion', function (done) { - runner.run({language: 'python', - code: 'a.fail()', - testFramework: 'cw-2'}, - function (buffer) { - console.log(buffer); - expect(buffer.stderr).to.not.contain('File '); - expect(buffer.stderr).to.not.contain(', line '); - expect(buffer.stderr).to.not.contain('most recent call last'); - done(); - }); - }); - }); - describe('unittest', function () { - it('should handle a basic assertion', function (done) { - runner.run({language: 'python', - code: 'a = 1', - fixture: [ - 'class Test(unittest.TestCase):', - ' def test_assert(self):', - ' self.assertEqual(a, 1)' - ].join('\n'), - testFramework: 'unittest'}, - function (buffer) { - console.log(buffer); - expect(buffer.stdout).to.equal('Test Passed\n'); - done(); - }); - }); - it('should handle a failed assetion', function (done) { - runner.run({language: 'python', - code: 'a = 1', - fixture: [ - 'class Test(unittest.TestCase):', - ' def test_assert(self):', - ' self.assertEqual(a, 2, "test failed")' - ].join('\n'), - testFramework: 'unittest'}, - function (buffer) { - console.log(buffer); - expect(buffer.stdout).to.contain(''); - expect(buffer.stdout).to.contain('test failed'); - done(); - }); + it('should handle a failed assertion', function (done) { + runner.run({ + language: 'python', + languageVersion: lv, + code: 'a.fail()', + testFramework: 'cw-2'}, + function (buffer) { + console.log(buffer); + expect(buffer.stderr).to.not.contain('File '); + expect(buffer.stderr).to.not.contain(', line '); + expect(buffer.stderr).to.not.contain('most recent call last'); + done(); + }); + }); }); - it('should handle a failed assetion', function (done) { - runner.run({language: 'python', - code: 'a = 1', - fixture: [ - 'class Test(unittest.TestCase):', - ' def test_assert(self):', - ' raise Exception("exceptions are my favorite, I always throw them")' - ].join('\n'), - testFramework: 'unittest'}, - function (buffer) { - console.log(buffer); - expect(buffer.stdout).to.equal('Unhandled Exception: exceptions are my favorite, I always throw them\n'); - done(); - }); + describe('unittest', function () { + it('should handle a basic assertion', function (done) { + runner.run({ + language: 'python', + languageVersion: lv, + code: 'a = 1', + fixture: [ + 'class Test(unittest.TestCase):', + ' def test_assert(self):', + ' self.assertEqual(a, 1)' + ].join('\n'), + testFramework: 'unittest'}, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Test Passed\n'); + done(); + }); + }); + it('should handle a failed assetion', function (done) { + runner.run({ + language: 'python', + languageVersion: lv, + code: 'a = 1', + fixture: [ + 'class Test(unittest.TestCase):', + ' def test_assert(self):', + ' self.assertEqual(a, 2, "test failed")' + ].join('\n'), + testFramework: 'unittest'}, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.contain(''); + expect(buffer.stdout).to.contain('test failed'); + done(); + }); + }); + it('should handle a failed assetion', function (done) { + runner.run({ + language: 'python', + code: 'a = 1', + languageVersion: lv, + fixture: [ + 'class Test(unittest.TestCase):', + ' def test_assert(self):', + ' raise Exception("exceptions are my favorite, I always throw them")' + ].join('\n'), + testFramework: 'unittest'}, + function (buffer) { + console.log(buffer); + expect(buffer.stdout).to.equal('Unhandled Exception: exceptions are my favorite, I always throw them\n'); + done(); + }); + }); }); }); + }); diff --git a/test/runners/typescript_spec.js b/test/runners/typescript_spec.js index 7150ada2..fd870adb 100644 --- a/test/runners/typescript_spec.js +++ b/test/runners/typescript_spec.js @@ -3,21 +3,32 @@ var runner = require('../runner'); describe( 'typescript runner', function(){ - //describe( '.run', function(){ - // it( 'should handle basic code evaluation', function(done){ - // runner.run({language: 'typescript', code: 'console.log(42)'}, function(buffer) { - // expect(buffer.stdout).to.equal('42\n'); - // done(); - // }); - // }); - //}); + describe( '.run', function(){ + it( 'should handle basic code evaluation', function(done){ + runner.run({language: 'typescript', code: 'console.log(42)'}, function(buffer) { + expect(buffer.stdout).to.equal('42\n'); + done(); + }); + }); + }); describe('mocha bdd', function() { it( 'should handle outputting objects', function(done){ runner.run({ language: 'typescript', - code: 'interface B { b:number }; var a:B = {b: 3};exports.a = a;', - fixture: 'var assert = require("chai").assert;describe("test", function(){it("should be 3", function(){assert.equal(3, solution.a.b);})});', + code: ` + export interface B { + b:number + }; + export var a:B = {b: 3}; + `, + fixture: ` + import {assert} from "chai"; + describe("test", function(){ + it("should be 3", function(){ + assert.equal(3, solution.a.b); + }) + });`, testFramework: 'mocha_bdd'}, function(buffer) { expect(buffer.stdout).to.contain(''); @@ -27,8 +38,8 @@ describe( 'typescript runner', function(){ it( 'should handle failures', function(done){ runner.run({ language: 'typescript', - code: 'exports.a = {b: 2};', - fixture: 'var assert = require("chai").assert;describe("test", function(){describe("failures", function(){it("should be 1", function(){assert.equal(1, solution.a.b);})})});', + code: 'export var a = {b: 2};', + fixture: 'import {assert} from "chai";describe("test", function(){describe("failures", function(){it("should be 1", function(){assert.equal(1, solution.a.b);})})});', testFramework: 'mocha_bdd'}, function(buffer) { expect(buffer.stdout).to.contain(''); @@ -38,8 +49,8 @@ describe( 'typescript runner', function(){ it( 'should handle errors', function(done){ runner.run({ language: 'typescript', - code: 'exports.a = {b: 2};', - fixture: 'describe("test", function(){describe("failures", function(){it("should be 1", function(){throw new Error("test error");})})});', + code: 'export var a = {b: 2};', + fixture: 'import {assert} from "chai"; describe("test", function(){describe("failures", function(){it("should be 1", function(){throw new Error("test error");})})});', testFramework: 'mocha_bdd'}, function(buffer) { expect(buffer.stdout).to.contain(''); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..944a5114 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs" + } +} \ No newline at end of file