Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca0d385a6c | |||
| 520e1d5f08 | |||
| 02423780a9 | |||
| be92d2f254 | |||
| 443a613b3a | |||
| 83ee5908f2 | |||
| 8dc3d2517b | |||
| 668c4fb878 | |||
| c65c6cce3f | |||
| 3d3796c1e9 | |||
| 075b354a3b | |||
| 30dbf04781 | |||
| e3a54485db | |||
| 8e23642ea2 | |||
| 88ac06875f | |||
| c68bc08a0d | |||
| 6cfbc2653a | |||
| b65135acef | |||
| e2f63da5c0 | |||
| 78e7e28ff9 | |||
| 03d387bf8e | |||
| e0f50b4eec | |||
| 9ed9418488 | |||
| ef0fc290b9 | |||
| edc65b5c19 | |||
| 715f4af179 | |||
| 3bd0fa8b32 | |||
| c295ef085f | |||
| c1ad3c8057 | |||
| af92e32822 | |||
| 2954e7ea66 | |||
| 8eefee56ff | |||
| fd93073146 | |||
| a9a8e40321 | |||
| e66aed5b99 | |||
| 7d4d170ce4 | |||
| eb860c0899 | |||
| b6d3de7123 | |||
| f058970169 | |||
| 77825f8d35 | |||
| 3789a30955 | |||
| e6ad93ea01 | |||
| 2bf4fc6fd3 | |||
| b64202bc91 | |||
| c2f1e62031 | |||
| 08e9a11a34 | |||
| 1fd2edee2a | |||
| 3c9c2f5cf2 | |||
| 16487a088e | |||
| 309d2b49a1 | |||
| 645ac73844 | |||
| b454b02147 |
@@ -0,0 +1,14 @@
|
||||
root = true
|
||||
|
||||
[*.js]
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[spec/expected/*.js]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
+1
-2
@@ -4,7 +4,6 @@
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
npm-debug.log
|
||||
sauce_connect.log*
|
||||
.idea
|
||||
yarn-error.log
|
||||
node_modules
|
||||
@@ -15,7 +14,7 @@ node_modules
|
||||
lib/handlebars/compiler/parser.js
|
||||
/coverage/
|
||||
/dist/
|
||||
/integration-testing/*/dist/
|
||||
/tests/integration/*/dist/
|
||||
|
||||
# Third-party or files that must remain unchanged
|
||||
/spec/expected/
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
open-pull-requests-limit: 0
|
||||
schedule:
|
||||
interval: weekly
|
||||
allow:
|
||||
- dependency-type: production
|
||||
@@ -0,0 +1,89 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request: {}
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
|
||||
test:
|
||||
name: Test (Node)
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
operating-system: ['ubuntu-latest', 'windows-latest']
|
||||
# https://nodejs.org/en/about/releases/
|
||||
node-version: ['10', '12', '14', '16', '18', '20']
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Test
|
||||
run: npm run test
|
||||
|
||||
- name: Test (Integration)
|
||||
# https://github.com/webpack/webpack/issues/14532
|
||||
if: ${{ matrix.node-version != '18' && matrix.node-version != '20' }}
|
||||
run: |
|
||||
cd ./tests/integration/rollup-test && ./test.sh && cd -
|
||||
cd ./tests/integration/webpack-babel-test && ./test.sh && cd -
|
||||
cd ./tests/integration/webpack-test && ./test.sh && cd -
|
||||
|
||||
browser:
|
||||
name: Test (Browser)
|
||||
runs-on: 'ubuntu-20.04'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright
|
||||
run: |
|
||||
npx playwright install-deps
|
||||
npx playwright install
|
||||
|
||||
- name: Build
|
||||
run: npx grunt prepare
|
||||
|
||||
- name: Test
|
||||
run: npm run test:browser
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
publish-aws-s3:
|
||||
name: Publish to AWS S3
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: 'builds.handlebarsjs.com.s3.amazonaws.com'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
git config --global user.email "release@handlebarsjs.com"
|
||||
git config --global user.name "handlebars-lang"
|
||||
npm run publish:aws
|
||||
env:
|
||||
S3_BUCKET_NAME: "builds.handlebarsjs.com"
|
||||
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
|
||||
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
|
||||
+2
-3
@@ -4,7 +4,6 @@
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
npm-debug.log
|
||||
sauce_connect.log*
|
||||
.idea
|
||||
/yarn-error.log
|
||||
/yarn.lock
|
||||
@@ -16,5 +15,5 @@ node_modules
|
||||
lib/handlebars/compiler/parser.js
|
||||
/coverage/
|
||||
/dist/
|
||||
/integration-testing/*/dist/
|
||||
/spec/tmp/*
|
||||
/tests/integration/*/dist/
|
||||
/spec/tmp/*
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[submodule "spec/mustache"]
|
||||
path = spec/mustache
|
||||
url = git://github.com/mustache/spec.git
|
||||
url = https://github.com/mustache/spec.git
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ node_modules
|
||||
lib/handlebars/compiler/parser.js
|
||||
/coverage/
|
||||
/dist/
|
||||
/integration-testing/*/dist/
|
||||
/tests/integration/*/dist/
|
||||
|
||||
# Third-party or files that must remain unchanged
|
||||
/spec/expected/
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
language: node_js
|
||||
jobs:
|
||||
include:
|
||||
- stage: test
|
||||
name: check javascript (eslint)
|
||||
node_js: lts/*
|
||||
script: npm run lint
|
||||
- stage: test
|
||||
name: check formatting (prettier)
|
||||
node_js: lts/*
|
||||
script: npm run check-format
|
||||
- stage: test
|
||||
name: check typescript definitions (dtslint)
|
||||
node_js: lts/*
|
||||
script: npm run dtslint
|
||||
- stage: test
|
||||
name: extensive tests and publish to aws
|
||||
script: npm run extensive-tests-and-publish-to-aws
|
||||
env:
|
||||
- S3_BUCKET_NAME=builds.handlebarsjs.com
|
||||
- secure: ckyEe5dzjdFDjmZ6wIrhGm0CFBEnKq8c1dYptfgVV/Q5/nJFGzu8T0yTjouS/ERxzdT2H327/63VCxhFnLCRHrsh4rlW/rCy4XI3O/0TeMLgFPa4TXkO8359qZ4CB44TBb3NsJyQXNMYdJpPLTCVTMpuiqqkFFOr+6OeggR7ufA=
|
||||
- secure: Nm4AgSfsgNB21kgKrF9Tl7qVZU8YYREhouQunFracTcZZh2NZ2XH5aHuSiXCj88B13Cr/jGbJKsZ4T3QS3wWYtz6lkyVOx3H3iI+TMtqhD9RM3a7A4O+4vVN8IioB2YjhEu0OKjwgX5gp+0uF+pLEi7Hpj6fupD3AbbL5uYcKg8=
|
||||
- SAUCE_USERNAME=handlebars
|
||||
- secure: 1VkLQhbsEug4ZMQ52tTOus/WLvW3Etqe7GbCzZfzsI8d2ygJPjFfzU8fNm4pVVwoTI21MaM5AQq7SVPu8DWN1YbDjJycMdY1zO3DsB9aZBxTal98fIB7ZIUce9r5z2EP6mETrsbYjZkeckzIBI0A4UVa+F2BO4KbRDXP1Db3u3I=
|
||||
node_js: '10'
|
||||
- stage: test
|
||||
name: test with latest nodejs-lts
|
||||
node_js: lts/*
|
||||
script: npm run test
|
||||
- stage: test
|
||||
name: test with active nodejs
|
||||
node_js: node
|
||||
script: npm run test
|
||||
cache: npm
|
||||
email:
|
||||
on_failure: change
|
||||
on_success: never
|
||||
git:
|
||||
depth: 100
|
||||
+101
-37
@@ -2,13 +2,13 @@
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Please see our [FAQ](https://github.com/wycats/handlebars.js/blob/master/FAQ.md) for common issues that people run into.
|
||||
Please see our [FAQ](https://github.com/handlebars-lang/handlebars.js/blob/master/FAQ.md) for common issues that people run into.
|
||||
|
||||
Should you run into other issues with the project, please don't hesitate to let us know by filing an [issue][issue]! In general we are going to ask for an example of the problem failing, which can be as simple as a jsfiddle/jsbin/etc. We've put together a jsfiddle [template][jsfiddle] to ease this. (We will keep this link up to date as new releases occur, so feel free to check back here)
|
||||
|
||||
Pull requests containing only failing tests demonstrating the issue are welcomed and this also helps ensure that your issue won't regress in the future once it's fixed.
|
||||
|
||||
Documentation issues on the handlebarsjs.com site should be reported on [handlebars-site](https://github.com/wycats/handlebars-site).
|
||||
Documentation issues on the [handlebarsjs.com](https://handlebarsjs.com) site should be reported on [handlebars-lang/docs](https://github.com/handlebars-lang/docs).
|
||||
|
||||
## Branches
|
||||
|
||||
@@ -47,9 +47,9 @@ You can also run our set of benchmarks with `grunt bench`.
|
||||
The `grunt dev` implements watching for tests and allows for in browser testing at `http://localhost:9999/spec/`.
|
||||
|
||||
If you notice any problems, please report them to the GitHub issue tracker at
|
||||
[http://github.com/wycats/handlebars.js/issues](http://github.com/wycats/handlebars.js/issues).
|
||||
[http://github.com/handlebars-lang/handlebars.js/issues](http://github.com/handlebars-lang/handlebars.js/issues).
|
||||
|
||||
##Running Tests
|
||||
## Running Tests
|
||||
|
||||
To run tests locally, first install all dependencies.
|
||||
|
||||
@@ -78,49 +78,107 @@ We do linting and formatting in two phases:
|
||||
|
||||
- Committed files are linted and formatted in a pre-commit hook. In this stage eslint-errors are forbidden,
|
||||
while warnings are allowed.
|
||||
- The travis-ci job also lints all files and checks if they are formatted correctly. In this stage, warnings
|
||||
- The GitHub CI job also lints all files and checks if they are formatted correctly. In this stage, warnings
|
||||
are forbidden.
|
||||
|
||||
You can use the following scripts to make sure that the travis-job does not fail:
|
||||
You can use the following scripts to make sure that the CI job does not fail:
|
||||
|
||||
- **npm run lint** will run `eslint` and fail on warnings
|
||||
- **npm run format** will run `prettier` on all files
|
||||
- **npm run check-before-pull-request** will perform all most checks that travis does in its build-job, excluding the "integration-test".
|
||||
- **npm run integration-test** will run integration tests (using old NodeJS versions and integrations with webpack, babel and so on)
|
||||
- **npm run check-before-pull-request** will perform all most checks that our CI job does in its build-job, excluding the "integration-test".
|
||||
- **npm run test:integration** will run integration tests (using old NodeJS versions and integrations with webpack, babel and so on)
|
||||
These tests only work on a Linux-machine with `nvm` installed (for running tests in multiple versions of NodeJS).
|
||||
|
||||
## Ember testing
|
||||
|
||||
The current ember distribution should be tested as part of the handlebars release process. This requires building the `handlebars-source` gem locally and then executing the ember test script.
|
||||
|
||||
```sh
|
||||
npm link
|
||||
grunt build release
|
||||
cp dist/*.js $emberRepoDir/bower_components/handlebars/
|
||||
|
||||
cd $emberRepoDir
|
||||
npm link handlebars
|
||||
npm test
|
||||
```
|
||||
|
||||
## Releasing the latest version
|
||||
|
||||
Before attempting the release Handlebars, please make sure that you have the following authorizations:
|
||||
|
||||
- Push-access to [handlebars-lang/handlebars.js](https://github.com/handlebars-lang/handlebars.js/)
|
||||
- Publishing rights on npmjs.com for the [handlebars](https://www.npmjs.com/package/handlebars) package
|
||||
- Publishing rights on rubygems for the [handlebars-source](https://rubygems.org/gems/handlebars-source) package
|
||||
- Push-access to the repo for legacy package managers: [components/handlebars.js](https://github.com/components/handlebars.js)
|
||||
- Push-access to the production-repo of the handlebars site: [handlebars-lang/docs](https://github.com/handlebars-lang/docs)
|
||||
|
||||
_When releasing a previous version of Handlebars, please look into the CONTRIBUNG.md in the corresponding branch._
|
||||
|
||||
Handlebars utilizes the [release yeoman generator][generator-release] to perform most release tasks.
|
||||
A full release via Docker may be completed with the following:
|
||||
|
||||
A full release may be completed with the following:
|
||||
1. Create a `Dockerfile` in this folder for releasing
|
||||
```Dockerfile
|
||||
FROM node:10-slim
|
||||
|
||||
ENV EDITOR=vim
|
||||
|
||||
# Update stretch repositories
|
||||
RUN sed -i -e 's/deb.debian.org/archive.debian.org/g' \
|
||||
-e 's|security.debian.org|archive.debian.org/|g' \
|
||||
-e '/stretch-updates/d' /etc/apt/sources.list
|
||||
|
||||
# Install release dependencies
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y git vim
|
||||
|
||||
# Work around deprecated npm dependency install via unauthenticated git-protocol:
|
||||
# https://github.com/kpdecker/generator-release/blob/87aab9b84c9f083635c3fcc822f18acce1f48736/package.json#L31
|
||||
RUN git config --system url."https://github.com/".insteadOf git://github.com/
|
||||
|
||||
# Configure git
|
||||
RUN git config --system user.email "release@handlebarsjs.com"
|
||||
RUN git config --system user.name "handlebars-lang"
|
||||
|
||||
RUN mkdir /home/node/.config
|
||||
RUN mkdir /home/node/.ssh
|
||||
RUN mkdir /home/node/tmp
|
||||
|
||||
# Generate config for yo generator-release:
|
||||
# https://github.com/kpdecker/generator-release#example
|
||||
# You have to add a valid GitHub OAuth token!
|
||||
RUN echo "module.exports = {\n auth: 'oauth',\n token: 'GitHub OAuth token'\n};" > /home/node/.config/generator-release
|
||||
RUN chown -R node:node /home/node/.config
|
||||
|
||||
# Add the generated key to GitHub: https://github.com/settings/keys
|
||||
RUN ssh-keygen -q -t ed25519 -N '' -f /home/node/.ssh/id_ed25519 -C "release@handlebarsjs.com"
|
||||
RUN chmod 0600 /home/node/.ssh/id_ed25519*
|
||||
RUN chown node:node /home/node/.ssh/id_ed25519*
|
||||
```
|
||||
2. Build and run the Docker image
|
||||
```bash
|
||||
docker build --tag handlebars:release .
|
||||
docker run --rm --interactive --tty \
|
||||
--volume $PWD:/app \
|
||||
--workdir /app \
|
||||
--user $(id -u):$(id -g) \
|
||||
--env NPM_CONFIG_PREFIX=/home/node/.npm-global \
|
||||
handlebars:release bash -c 'export PATH=$PATH:/home/node/.npm-global/bin; bash'
|
||||
```
|
||||
* Add SSH key to GitHub: `cat /home/node/.ssh/id_ed25519.pub` (https://github.com/settings/keys)
|
||||
* Add GitHub API token: `vi /home/node/.config/generator-release`
|
||||
* Execute the following steps:
|
||||
```bash
|
||||
npm ci
|
||||
npm install -g yo@1 grunt@1 generator-release
|
||||
npm run release
|
||||
yo release
|
||||
npm login
|
||||
npm publish
|
||||
yo release:publish components handlebars.js dist/components/
|
||||
```
|
||||
6. Publish Ruby `handlebars-source` gem:
|
||||
```bash
|
||||
docker run --rm --interactive --tty \
|
||||
--volume $PWD:/app \
|
||||
--workdir /app \
|
||||
--user $(id -u):$(id -g) \
|
||||
ruby:3.2-slim bash
|
||||
```
|
||||
* Execute the following steps:
|
||||
```bash
|
||||
cd dist/components/
|
||||
gem build handlebars-source.gemspec
|
||||
gem push handlebars-source-*.gem
|
||||
```
|
||||
|
||||
```
|
||||
npm ci
|
||||
yo release
|
||||
npm publish
|
||||
yo release:publish components handlebars.js dist/components/
|
||||
|
||||
cd dist/components/
|
||||
gem build handlebars-source.gemspec
|
||||
gem push handlebars-source-*.gem
|
||||
```
|
||||
### After the release
|
||||
|
||||
After the release, you should check that all places have really been updated. Especially verify that the `latest`-tags
|
||||
in those places still point to the latest version
|
||||
@@ -130,9 +188,15 @@ in those places still point to the latest version
|
||||
- [The AWS S3 Bucket](https://s3.amazonaws.com/builds.handlebarsjs.com) (check latest-tag)
|
||||
- [RubyGems](https://rubygems.org/gems/handlebars-source)
|
||||
|
||||
When everything is OK, the handlebars site needs to be updated to point to the new version numbers. The jsfiddle link should be updated to point to the most recent distribution for all instances in our documentation.
|
||||
When everything is OK, the **handlebars site** needs to be updated.
|
||||
|
||||
Go to the master branch of the repo [handlebars-lang/docs](https://github.com/handlebars-lang/docs/tree/master)
|
||||
and make a minimal change to the README. This will invoke a github-action that redeploys
|
||||
the site, fetching the latest version-number from the npm-registry.
|
||||
(note that the default-branch of this repo is not the master and regular changes are done
|
||||
in the `handlebars-lang/docs`-repo).
|
||||
|
||||
[generator-release]: https://github.com/walmartlabs/generator-release
|
||||
[pull-request]: https://github.com/wycats/handlebars.js/pull/new/master
|
||||
[issue]: https://github.com/wycats/handlebars.js/issues/new
|
||||
[pull-request]: https://github.com/handlebars-lang/handlebars.js/pull/new/master
|
||||
[issue]: https://github.com/handlebars-lang/handlebars.js/issues/new
|
||||
[jsfiddle]: https://jsfiddle.net/9D88g/180/
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
1. How can I file a bug report:
|
||||
|
||||
See our guidelines on [reporting issues](https://github.com/wycats/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
|
||||
See our guidelines on [reporting issues](https://github.com/handlebars-lang/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
|
||||
|
||||
1. Why isn't my Mustache template working?
|
||||
|
||||
Handlebars deviates from Mustache slightly on a few behaviors. These variations are documented in our [readme](https://github.com/wycats/handlebars.js#differences-between-handlebarsjs-and-mustache).
|
||||
Handlebars deviates from Mustache slightly on a few behaviors. These variations are documented in our [readme](https://github.com/handlebars-lang/handlebars.js#differences-between-handlebarsjs-and-mustache).
|
||||
|
||||
1. Why is it slower when compiling?
|
||||
|
||||
@@ -36,16 +36,18 @@
|
||||
```sh
|
||||
handlebars --version
|
||||
```
|
||||
|
||||
If using the integrated precompiler and
|
||||
|
||||
```javascript
|
||||
console.log(Handlebars.VERSION);
|
||||
```
|
||||
|
||||
On the client side.
|
||||
|
||||
We include the built client libraries in the npm package for those who want to be certain that they are using the same client libraries as the compiler.
|
||||
|
||||
Should these match, please file an issue with us, per our [issue filing guidelines](https://github.com/wycats/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
|
||||
Should these match, please file an issue with us, per our [issue filing guidelines](https://github.com/handlebars-lang/handlebars.js/blob/master/CONTRIBUTING.md#reporting-issues).
|
||||
|
||||
1. Why doesn't IE like the `default` name in the AMD module?
|
||||
|
||||
@@ -53,8 +55,8 @@
|
||||
|
||||
1. How do I load the runtime library when using AMD?
|
||||
|
||||
There are two options for loading under AMD environments. The first is to use the `handlebars.runtime.amd.js` file. This may require a [path mapping](https://github.com/wycats/handlebars.js/blob/master/spec/amd-runtime.html#L31) as well as access via the `default` field.
|
||||
There are two options for loading under AMD environments. The first is to use the `handlebars.runtime.amd.js` file. This may require a [path mapping](https://github.com/handlebars-lang/handlebars.js/blob/master/spec/amd-runtime.html#L31) as well as access via the `default` field.
|
||||
|
||||
The other option is to load the `handlebars.runtime.js` UMD build, which might not require path configuration and exposes the library as both the module root and the `default` field for compatibility.
|
||||
The other option is to load the `handlebars.runtime.js` UMD build, which might not require path configuration and exposes the library as both the module root and the `default` field for compatibility.
|
||||
|
||||
If not using ES6 transpilers or accessing submodules in the build the former option should be sufficient for most use cases.
|
||||
|
||||
+21
-82
@@ -7,7 +7,7 @@ module.exports = function(grunt) {
|
||||
'tmp',
|
||||
'dist',
|
||||
'lib/handlebars/compiler/parser.js',
|
||||
'integration-testing/**/node_modules'
|
||||
'/tests/integration/**/node_modules'
|
||||
],
|
||||
|
||||
copy: {
|
||||
@@ -165,54 +165,10 @@ module.exports = function(grunt) {
|
||||
}
|
||||
}
|
||||
},
|
||||
'saucelabs-mocha': {
|
||||
all: {
|
||||
options: {
|
||||
build: process.env.TRAVIS_JOB_ID,
|
||||
urls: [
|
||||
'http://localhost:9999/spec/?headless=true',
|
||||
'http://localhost:9999/spec/amd.html?headless=true'
|
||||
],
|
||||
detailedError: true,
|
||||
concurrency: 4,
|
||||
browsers: [
|
||||
{ browserName: 'chrome' },
|
||||
{ browserName: 'firefox', platform: 'Linux' },
|
||||
// {browserName: 'safari', version: 9, platform: 'OS X 10.11'},
|
||||
// {browserName: 'safari', version: 8, platform: 'OS X 10.10'},
|
||||
{
|
||||
browserName: 'internet explorer',
|
||||
version: 11,
|
||||
platform: 'Windows 8.1'
|
||||
},
|
||||
{
|
||||
browserName: 'internet explorer',
|
||||
version: 10,
|
||||
platform: 'Windows 8'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
sanity: {
|
||||
options: {
|
||||
build: process.env.TRAVIS_JOB_ID,
|
||||
urls: [
|
||||
'http://localhost:9999/spec/umd.html?headless=true',
|
||||
'http://localhost:9999/spec/amd-runtime.html?headless=true',
|
||||
'http://localhost:9999/spec/umd-runtime.html?headless=true'
|
||||
],
|
||||
detailedError: true,
|
||||
concurrency: 2,
|
||||
browsers: [{ browserName: 'chrome' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
bgShell: {
|
||||
shell: {
|
||||
integrationTests: {
|
||||
cmd: './integration-testing/run-integration-tests.sh',
|
||||
bg: false,
|
||||
fail: true
|
||||
command: './tests/integration/run-integration-tests.sh'
|
||||
}
|
||||
},
|
||||
|
||||
@@ -237,23 +193,15 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-babel');
|
||||
grunt.loadNpmTasks('grunt-bg-shell');
|
||||
grunt.loadNpmTasks('@knappi/grunt-saucelabs');
|
||||
grunt.loadNpmTasks('grunt-shell');
|
||||
grunt.loadNpmTasks('grunt-webpack');
|
||||
|
||||
grunt.task.loadTasks('tasks');
|
||||
|
||||
this.registerTask(
|
||||
'build',
|
||||
'Builds a distributable version of the current project',
|
||||
['parser', 'node', 'globals']
|
||||
);
|
||||
|
||||
this.registerTask('node', ['babel:cjs']);
|
||||
this.registerTask('globals', ['webpack']);
|
||||
|
||||
this.registerTask('release', 'Build final packages', [
|
||||
'amd',
|
||||
grunt.registerTask('node', ['babel:cjs']);
|
||||
grunt.registerTask('amd', ['babel:amd', 'requirejs']);
|
||||
grunt.registerTask('globals', ['webpack']);
|
||||
grunt.registerTask('release', 'Build final packages', [
|
||||
'uglify',
|
||||
'test:min',
|
||||
'copy:dist',
|
||||
@@ -261,38 +209,29 @@ module.exports = function(grunt) {
|
||||
'copy:cdnjs'
|
||||
]);
|
||||
|
||||
this.registerTask('amd', ['babel:amd', 'requirejs']);
|
||||
|
||||
this.registerTask('test', ['test:bin', 'test:cov']);
|
||||
|
||||
grunt.registerTask('bench', ['metrics']);
|
||||
|
||||
if (process.env.SAUCE_ACCESS_KEY) {
|
||||
grunt.registerTask('sauce', ['concat:tests', 'connect', 'saucelabs-mocha']);
|
||||
} else {
|
||||
grunt.registerTask('sauce', []);
|
||||
}
|
||||
|
||||
// Requires secret properties (saucelabs-credentials etc.) from .travis.yaml
|
||||
// Requires secret properties from .travis.yaml
|
||||
grunt.registerTask('extensive-tests-and-publish-to-aws', [
|
||||
'default',
|
||||
'bgShell:integrationTests',
|
||||
'sauce',
|
||||
'shell:integrationTests',
|
||||
'metrics',
|
||||
'publish-to-aws'
|
||||
]);
|
||||
grunt.registerTask('on-file-change', [
|
||||
'build',
|
||||
'amd',
|
||||
'concat:tests',
|
||||
'test'
|
||||
]);
|
||||
|
||||
grunt.registerTask('on-file-change', ['build', 'concat:tests', 'test']);
|
||||
|
||||
// === Primary tasks ===
|
||||
grunt.registerTask('dev', ['clean', 'connect', 'watch']);
|
||||
grunt.registerTask('default', ['clean', 'build', 'test', 'release']);
|
||||
grunt.registerTask('test', ['test:bin', 'test:cov']);
|
||||
grunt.registerTask('bench', ['metrics']);
|
||||
grunt.registerTask('prepare', ['build', 'concat:tests']);
|
||||
grunt.registerTask(
|
||||
'build',
|
||||
'Builds a distributable version of the current project',
|
||||
['parser', 'node', 'amd', 'globals']
|
||||
);
|
||||
grunt.registerTask('integration-tests', [
|
||||
'default',
|
||||
'bgShell:integrationTests'
|
||||
'shell:integrationTests'
|
||||
]);
|
||||
};
|
||||
|
||||
+15
-16
@@ -1,17 +1,18 @@
|
||||
[](https://travis-ci.org/wycats/handlebars.js)
|
||||
[](https://ci.appveyor.com/project/wycats/handlebars-js)
|
||||
[](https://saucelabs.com/u/handlebars)
|
||||
[](https://github.com/handlebars-lang/handlebars.js/actions/workflows/ci.yml)
|
||||
[](https://www.jsdelivr.com/package/npm/handlebars)
|
||||
[](https://www.npmjs.com/package/handlebars)
|
||||
[](https://www.npmjs.com/package/handlebars)
|
||||
[](https://bundlephobia.com/package/handlebars)
|
||||
[](https://packagephobia.com/result?p=handlebars)
|
||||
|
||||
Handlebars.js
|
||||
=============
|
||||
|
||||
Handlebars.js is an extension to the [Mustache templating
|
||||
language](http://mustache.github.com/) created by Chris Wanstrath.
|
||||
Handlebars.js and Mustache are both logicless templating languages that
|
||||
keep the view and the code separated like we all know they should be.
|
||||
Handlebars provides the power necessary to let you build **semantic templates** effectively with no frustration.
|
||||
Handlebars is largely compatible with Mustache templates. In most cases it is possible to swap out Mustache with Handlebars and continue using your current templates.
|
||||
|
||||
Checkout the official Handlebars docs site at
|
||||
[https://handlebarsjs.com/](https://handlebarsjs.com) and the live demo at [http://tryhandlebarsjs.com/](http://tryhandlebarsjs.com/).
|
||||
[handlebarsjs.com](https://handlebarsjs.com) and try our [live demo](https://handlebarsjs.com/playground.html).
|
||||
|
||||
Installing
|
||||
----------
|
||||
@@ -22,7 +23,7 @@ Usage
|
||||
-----
|
||||
In general, the syntax of Handlebars.js templates is a superset
|
||||
of Mustache templates. For basic syntax, check out the [Mustache
|
||||
manpage](http://mustache.github.com/mustache.5.html).
|
||||
manpage](https://mustache.github.io/mustache.5.html).
|
||||
|
||||
Once you have a template, use the `Handlebars.compile` method to compile
|
||||
the template into a function. The generated function takes a context
|
||||
@@ -64,7 +65,7 @@ templates easier and also changes a tiny detail of how partials work.
|
||||
- [Literal Values](https://handlebarsjs.com/guide/expressions.html#literal-segments)
|
||||
- [Delimited Comments](https://handlebarsjs.com/guide/#template-comments)
|
||||
|
||||
Block expressions have the same syntax as mustache sections but should not be confused with one another. Sections are akin to an implicit `each` or `with` statement depending on the input data and helpers are explicit pieces of code that are free to implement whatever behavior they like. The [mustache spec](http://mustache.github.io/mustache.5.html) defines the exact behavior of sections. In the case of name conflicts, helpers are given priority.
|
||||
Block expressions have the same syntax as mustache sections but should not be confused with one another. Sections are akin to an implicit `each` or `with` statement depending on the input data and helpers are explicit pieces of code that are free to implement whatever behavior they like. The [mustache spec](https://mustache.github.io/mustache.5.html) defines the exact behavior of sections. In the case of name conflicts, helpers are given priority.
|
||||
|
||||
### Compatibility
|
||||
|
||||
@@ -89,8 +90,6 @@ Handlebars has been designed to work in any ECMAScript 3 environment. This inclu
|
||||
Older versions and other runtimes are likely to work but have not been formally
|
||||
tested. The compiler requires `JSON.stringify` to be implemented natively or via a polyfill. If using the precompiler this is not necessary.
|
||||
|
||||
[](https://saucelabs.com/u/handlebars)
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
@@ -102,18 +101,18 @@ does have some big performance advantages. Justin Marney, a.k.a.
|
||||
[gotascii](http://github.com/gotascii), confirmed that with an
|
||||
[independent test](http://sorescode.com/2010/09/12/benchmarks.html). The
|
||||
rewritten Handlebars (current version) is faster than the old version,
|
||||
with many [performance tests](https://travis-ci.org/wycats/handlebars.js/builds/33392182#L538) being 5 to 7 times faster than the Mustache equivalent.
|
||||
with many performance tests being 5 to 7 times faster than the Mustache equivalent.
|
||||
|
||||
|
||||
Upgrading
|
||||
---------
|
||||
|
||||
See [release-notes.md](https://github.com/wycats/handlebars.js/blob/master/release-notes.md) for upgrade notes.
|
||||
See [release-notes.md](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md) for upgrade notes.
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
See [FAQ.md](https://github.com/wycats/handlebars.js/blob/master/FAQ.md) for known issues and common pitfalls.
|
||||
See [FAQ.md](https://github.com/handlebars-lang/handlebars.js/blob/master/FAQ.md) for known issues and common pitfalls.
|
||||
|
||||
|
||||
Handlebars in the Wild
|
||||
@@ -165,4 +164,4 @@ License
|
||||
-------
|
||||
Handlebars.js is released under the MIT license.
|
||||
|
||||
[pull-request]: https://github.com/wycats/handlebars.js/pull/new/master
|
||||
[pull-request]: https://github.com/handlebars-lang/handlebars.js/pull/new/master
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# Test against these versions of Node.js
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "10"
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
# Install scripts (runs after repo cloning)
|
||||
install:
|
||||
# Get the latest stable version of Node.js
|
||||
- ps: Install-Product node $env:nodejs_version $env:platform
|
||||
# Clone submodules (mustache spec)
|
||||
- cmd: git submodule update --init --recursive
|
||||
# Install modules
|
||||
- cmd: npm ci
|
||||
|
||||
|
||||
# Post-install test scripts
|
||||
test_script:
|
||||
# Output useful info for debugging
|
||||
- cmd: node --version
|
||||
- cmd: npm --version
|
||||
# Run tests
|
||||
- cmd: npm run test
|
||||
|
||||
# Don't actually build
|
||||
build: off
|
||||
|
||||
on_failure:
|
||||
- cmd: 7z a coverage.zip coverage
|
||||
- cmd: appveyor PushArtifact coverage.zip
|
||||
|
||||
|
||||
# Set build version format here instead of in the admin panel
|
||||
version: "{build}"
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"extends": "prettier",
|
||||
"globals": {
|
||||
"require": true
|
||||
},
|
||||
"rules": {
|
||||
// Disabling for tests, for now.
|
||||
"no-path-concat": 0,
|
||||
|
||||
"no-var": 0,
|
||||
"no-shadow": 0,
|
||||
"handle-callback-err": 0,
|
||||
"no-console": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
rules: {
|
||||
'no-console': 0,
|
||||
'no-var': 0
|
||||
}
|
||||
};
|
||||
+159
-107
@@ -1,115 +1,109 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const yargs = require('yargs')
|
||||
.usage('Precompile handlebar templates.\nUsage: $0 [template|directory]...')
|
||||
.option('f', {
|
||||
type: 'string',
|
||||
description: 'Output File',
|
||||
alias: 'output'
|
||||
})
|
||||
.option('map', {
|
||||
type: 'string',
|
||||
description: 'Source Map File'
|
||||
})
|
||||
.option('a', {
|
||||
type: 'boolean',
|
||||
description: 'Exports amd style (require.js)',
|
||||
alias: 'amd'
|
||||
})
|
||||
.option('c', {
|
||||
type: 'string',
|
||||
description: 'Exports CommonJS style, path to Handlebars module',
|
||||
alias: 'commonjs',
|
||||
default: null
|
||||
})
|
||||
.option('h', {
|
||||
type: 'string',
|
||||
description: 'Path to handlebar.js (only valid for amd-style)',
|
||||
alias: 'handlebarPath',
|
||||
default: ''
|
||||
})
|
||||
.option('k', {
|
||||
type: 'string',
|
||||
description: 'Known helpers',
|
||||
alias: 'known'
|
||||
})
|
||||
.option('o', {
|
||||
type: 'boolean',
|
||||
description: 'Known helpers only',
|
||||
alias: 'knownOnly'
|
||||
})
|
||||
.option('m', {
|
||||
type: 'boolean',
|
||||
description: 'Minimize output',
|
||||
alias: 'min'
|
||||
})
|
||||
.option('n', {
|
||||
type: 'string',
|
||||
description: 'Template namespace',
|
||||
alias: 'namespace',
|
||||
default: 'Handlebars.templates'
|
||||
})
|
||||
.option('s', {
|
||||
type: 'boolean',
|
||||
description: 'Output template function only.',
|
||||
alias: 'simple'
|
||||
})
|
||||
.option('N', {
|
||||
type: 'string',
|
||||
description:
|
||||
'Name of passed string templates. Optional if running in a simple mode. Required when operating on multiple templates.',
|
||||
alias: 'name'
|
||||
})
|
||||
.option('i', {
|
||||
type: 'string',
|
||||
description:
|
||||
'Generates a template from the passed CLI argument.\n"-" is treated as a special value and causes stdin to be read for the template value.',
|
||||
alias: 'string'
|
||||
})
|
||||
.option('r', {
|
||||
type: 'string',
|
||||
description:
|
||||
'Template root. Base value that will be stripped from template names.',
|
||||
alias: 'root'
|
||||
})
|
||||
.option('p', {
|
||||
type: 'boolean',
|
||||
description: 'Compiling a partial template',
|
||||
alias: 'partial'
|
||||
})
|
||||
.option('d', {
|
||||
type: 'boolean',
|
||||
description: 'Include data when compiling',
|
||||
alias: 'data'
|
||||
})
|
||||
.option('e', {
|
||||
type: 'string',
|
||||
description: 'Template extension.',
|
||||
alias: 'extension',
|
||||
default: 'handlebars'
|
||||
})
|
||||
.option('b', {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Removes the BOM (Byte Order Mark) from the beginning of the templates.',
|
||||
alias: 'bom'
|
||||
})
|
||||
.option('v', {
|
||||
type: 'boolean',
|
||||
description: 'Prints the current compiler version',
|
||||
alias: 'version'
|
||||
})
|
||||
.option('help', {
|
||||
type: 'boolean',
|
||||
description: 'Outputs this message'
|
||||
})
|
||||
.wrap(120);
|
||||
var argv = parseArgs({
|
||||
'f': {
|
||||
'type': 'string',
|
||||
'description': 'Output File',
|
||||
'alias': 'output'
|
||||
},
|
||||
'map': {
|
||||
'type': 'string',
|
||||
'description': 'Source Map File'
|
||||
},
|
||||
'a': {
|
||||
'type': 'boolean',
|
||||
'description': 'Exports amd style (require.js)',
|
||||
'alias': 'amd'
|
||||
},
|
||||
'c': {
|
||||
'type': 'string',
|
||||
'description': 'Exports CommonJS style, path to Handlebars module',
|
||||
'alias': 'commonjs',
|
||||
'default': null
|
||||
},
|
||||
'h': {
|
||||
'type': 'string',
|
||||
'description': 'Path to handlebar.js (only valid for amd-style)',
|
||||
'alias': 'handlebarPath',
|
||||
'default': ''
|
||||
},
|
||||
'k': {
|
||||
'type': 'string',
|
||||
'description': 'Known helpers',
|
||||
'alias': 'known'
|
||||
},
|
||||
'o': {
|
||||
'type': 'boolean',
|
||||
'description': 'Known helpers only',
|
||||
'alias': 'knownOnly'
|
||||
},
|
||||
'm': {
|
||||
'type': 'boolean',
|
||||
'description': 'Minimize output',
|
||||
'alias': 'min'
|
||||
},
|
||||
'n': {
|
||||
'type': 'string',
|
||||
'description': 'Template namespace',
|
||||
'alias': 'namespace',
|
||||
'default': 'Handlebars.templates'
|
||||
},
|
||||
's': {
|
||||
'type': 'boolean',
|
||||
'description': 'Output template function only.',
|
||||
'alias': 'simple'
|
||||
},
|
||||
'N': {
|
||||
'type': 'string',
|
||||
'description': 'Name of passed string templates. Optional if running in a simple mode. Required when operating on multiple templates.',
|
||||
'alias': 'name'
|
||||
},
|
||||
'i': {
|
||||
'type': 'string',
|
||||
'description': 'Generates a template from the passed CLI argument.\n"-" is treated as a special value and causes stdin to be read for the template value.',
|
||||
'alias': 'string'
|
||||
},
|
||||
'r': {
|
||||
'type': 'string',
|
||||
'description': 'Template root. Base value that will be stripped from template names.',
|
||||
'alias': 'root'
|
||||
},
|
||||
'p': {
|
||||
'type': 'boolean',
|
||||
'description': 'Compiling a partial template',
|
||||
'alias': 'partial'
|
||||
},
|
||||
'd': {
|
||||
'type': 'boolean',
|
||||
'description': 'Include data when compiling',
|
||||
'alias': 'data'
|
||||
},
|
||||
'e': {
|
||||
'type': 'string',
|
||||
'description': 'Template extension.',
|
||||
'alias': 'extension',
|
||||
'default': 'handlebars'
|
||||
},
|
||||
'b': {
|
||||
'type': 'boolean',
|
||||
'description': 'Removes the BOM (Byte Order Mark) from the beginning of the templates.',
|
||||
'alias': 'bom'
|
||||
},
|
||||
'v': {
|
||||
'type': 'boolean',
|
||||
'description': 'Prints the current compiler version',
|
||||
'alias': 'version'
|
||||
},
|
||||
'help': {
|
||||
'type': 'boolean',
|
||||
'description': 'Outputs this message'
|
||||
}
|
||||
});
|
||||
|
||||
const argv = yargs.argv;
|
||||
argv.files = argv._;
|
||||
delete argv._;
|
||||
|
||||
const Precompiler = require('../dist/cjs/precompiler');
|
||||
var Precompiler = require('../dist/cjs/precompiler');
|
||||
Precompiler.loadTemplates(argv, function(err, opts) {
|
||||
|
||||
if (err) {
|
||||
@@ -117,8 +111,66 @@ Precompiler.loadTemplates(argv, function(err, opts) {
|
||||
}
|
||||
|
||||
if (opts.help || (!opts.templates.length && !opts.version)) {
|
||||
yargs.showHelp();
|
||||
printUsage(argv._spec, 120);
|
||||
} else {
|
||||
Precompiler.cli(opts);
|
||||
}
|
||||
});
|
||||
|
||||
function pad(n) {
|
||||
var str = '';
|
||||
while (str.length < n) {
|
||||
str += ' ';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function parseArgs(spec) {
|
||||
var opts = { alias: {}, boolean: [], default: {}, string: [] };
|
||||
|
||||
Object.keys(spec).forEach(function (arg) {
|
||||
var opt = spec[arg];
|
||||
opts[opt.type].push(arg);
|
||||
if ('alias' in opt) opts.alias[arg] = opt.alias;
|
||||
if ('default' in opt) opts.default[arg] = opt.default;
|
||||
});
|
||||
|
||||
var argv = require('minimist')(process.argv.slice(2), opts);
|
||||
argv._spec = spec;
|
||||
return argv;
|
||||
}
|
||||
|
||||
function printUsage(spec, wrap) {
|
||||
var wordwrap = require('wordwrap');
|
||||
|
||||
console.log('Precompile handlebar templates.');
|
||||
console.log('Usage: handlebars [template|directory]...');
|
||||
|
||||
var opts = [];
|
||||
var width = 0;
|
||||
Object.keys(spec).forEach(function (arg) {
|
||||
var opt = spec[arg];
|
||||
|
||||
var name = (arg.length === 1 ? '-' : '--') + arg;
|
||||
if ('alias' in opt) name += ', --' + opt.alias;
|
||||
|
||||
var meta = '[' + opt.type + ']';
|
||||
if ('default' in opt) meta += ' [default: ' + JSON.stringify(opt.default) + ']';
|
||||
|
||||
opts.push({ name: name, desc: opt.description, meta: meta });
|
||||
if (name.length > width) width = name.length;
|
||||
});
|
||||
|
||||
console.log('Options:');
|
||||
opts.forEach(function (opt) {
|
||||
var desc = wordwrap(width + 4, wrap + 1)(opt.desc);
|
||||
|
||||
console.log(' %s%s%s%s%s',
|
||||
opt.name,
|
||||
pad(width - opt.name.length + 2),
|
||||
desc.slice(width + 4),
|
||||
pad(wrap - opt.meta.length - desc.split(/\n/).pop().length),
|
||||
opt.meta
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"version": "4.7.4",
|
||||
"version": "4.7.8",
|
||||
"main": "handlebars.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {}
|
||||
|
||||
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
|
||||
gem.date = Time.now.strftime("%Y-%m-%d")
|
||||
gem.description = %q{Handlebars.js source code wrapper for (pre)compilation gems.}
|
||||
gem.summary = %q{Handlebars.js source code wrapper}
|
||||
gem.homepage = "https://github.com/wycats/handlebars.js/"
|
||||
gem.homepage = "https://github.com/handlebars-lang/handlebars.js/"
|
||||
gem.version = package["version"].sub "-", "."
|
||||
gem.license = "MIT"
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>handlebars.js</id>
|
||||
<version>4.7.4</version>
|
||||
<version>4.7.8</version>
|
||||
<authors>handlebars.js Authors</authors>
|
||||
<licenseUrl>https://github.com/wycats/handlebars.js/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/wycats/handlebars.js/</projectUrl>
|
||||
<licenseUrl>https://github.com/handlebars-lang/handlebars.js/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/handlebars-lang/handlebars.js/</projectUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>Extension of the Mustache logicless template language</description>
|
||||
<releaseNotes></releaseNotes>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"version": "4.7.4",
|
||||
"version": "4.7.8",
|
||||
"license": "MIT",
|
||||
"jspm": {
|
||||
"main": "handlebars",
|
||||
|
||||
@@ -16,4 +16,4 @@ Decorators are executed when the block program is instantiated and are passed `(
|
||||
|
||||
Decorators may set values on `props` or return a modified function that wraps `program` in particular behaviors. If the decorator returns nothing, then `program` is left unaltered.
|
||||
|
||||
The [inline partial](https://github.com/wycats/handlebars.js/blob/master/lib/handlebars/decorators/inline.js) implementation provides an example of decorators being used for both metadata and wrapping behaviors.
|
||||
The [inline partial](https://github.com/handlebars-lang/handlebars.js/blob/master/lib/handlebars/decorators/inline.js) implementation provides an example of decorators being used for both metadata and wrapping behaviors.
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "webpack-test",
|
||||
"description": "Various tests with Handlebars and Webpack",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"test": "node dist/main.js"
|
||||
},
|
||||
"private": true,
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"handlebars": "file:../..",
|
||||
"handlebars-loader": "^1.7.1",
|
||||
"webpack": "^4.39.3",
|
||||
"webpack-cli": "^3.3.7"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
// Handlebars should not use node or browser-specific APIs
|
||||
'shared-node-browser': true,
|
||||
node: false,
|
||||
browser: false
|
||||
}
|
||||
};
|
||||
@@ -5,7 +5,7 @@ import { registerDefaultDecorators } from './decorators';
|
||||
import logger from './logger';
|
||||
import { resetLoggedProperties } from './internal/proto-access';
|
||||
|
||||
export const VERSION = '4.7.4';
|
||||
export const VERSION = '4.7.8';
|
||||
export const COMPILER_REVISION = 8;
|
||||
export const LAST_COMPATIBLE_COMPILER_REVISION = 7;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global define */
|
||||
/* global define, require */
|
||||
import { isArray } from '../utils';
|
||||
|
||||
let SourceNode;
|
||||
@@ -6,7 +6,7 @@ let SourceNode;
|
||||
try {
|
||||
/* istanbul ignore next */
|
||||
if (typeof define !== 'function' || !define.amd) {
|
||||
// We don't support this in AMD environments. For these environments, we asusme that
|
||||
// We don't support this in AMD environments. For these environments, we assume that
|
||||
// they are running on the browser and thus have no need for the source-map library.
|
||||
let SourceMap = require('source-map');
|
||||
SourceNode = SourceMap.SourceNode;
|
||||
|
||||
@@ -16,7 +16,12 @@ JavaScriptCompiler.prototype = {
|
||||
return this.internalNameLookup(parent, name);
|
||||
},
|
||||
depthedLookup: function(name) {
|
||||
return [this.aliasable('container.lookup'), '(depths, "', name, '")'];
|
||||
return [
|
||||
this.aliasable('container.lookup'),
|
||||
'(depths, ',
|
||||
JSON.stringify(name),
|
||||
')'
|
||||
];
|
||||
},
|
||||
|
||||
compilerInfo: function() {
|
||||
@@ -303,7 +308,7 @@ JavaScriptCompiler.prototype = {
|
||||
|
||||
if (appendOnly) {
|
||||
if (bufferStart) {
|
||||
bufferStart.prepend('return ');
|
||||
bufferStart.prepend('return ""+');
|
||||
bufferEnd.add(';');
|
||||
} else if (!sourceSeen) {
|
||||
this.source.push('return "";');
|
||||
@@ -532,7 +537,9 @@ JavaScriptCompiler.prototype = {
|
||||
|
||||
resolvePath: function(type, parts, i, falsy, strict) {
|
||||
if (this.options.strict || this.options.assumeObjects) {
|
||||
this.push(strictLookup(this.options.strict && strict, this, parts, type));
|
||||
this.push(
|
||||
strictLookup(this.options.strict && strict, this, parts, i, type)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1256,9 +1263,8 @@ JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
|
||||
);
|
||||
};
|
||||
|
||||
function strictLookup(requireTerminal, compiler, parts, type) {
|
||||
function strictLookup(requireTerminal, compiler, parts, i, type) {
|
||||
let stack = compiler.popStack(),
|
||||
i = 0,
|
||||
len = parts.length;
|
||||
if (requireTerminal) {
|
||||
len--;
|
||||
|
||||
@@ -63,9 +63,9 @@ export default function(instance) {
|
||||
execIteration(i, i, i === context.length - 1);
|
||||
}
|
||||
}
|
||||
} else if (global.Symbol && context[global.Symbol.iterator]) {
|
||||
} else if (typeof Symbol === 'function' && context[Symbol.iterator]) {
|
||||
const newContext = [];
|
||||
const iterator = context[global.Symbol.iterator]();
|
||||
const iterator = context[Symbol.iterator]();
|
||||
for (let it = iterator.next(); !it.done; it = iterator.next()) {
|
||||
newContext.push(it.value);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createNewLookupObject } from './create-new-lookup-object';
|
||||
import * as logger from '../logger';
|
||||
import logger from '../logger';
|
||||
|
||||
const loggedProperties = Object.create(null);
|
||||
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
/* global globalThis */
|
||||
export default function(Handlebars) {
|
||||
/* istanbul ignore next */
|
||||
let root = typeof global !== 'undefined' ? global : window,
|
||||
$Handlebars = root.Handlebars;
|
||||
// https://mathiasbynens.be/notes/globalthis
|
||||
(function() {
|
||||
if (typeof globalThis === 'object') return;
|
||||
Object.prototype.__defineGetter__('__magic__', function() {
|
||||
return this;
|
||||
});
|
||||
__magic__.globalThis = __magic__; // eslint-disable-line no-undef
|
||||
delete Object.prototype.__magic__;
|
||||
})();
|
||||
|
||||
const $Handlebars = globalThis.Handlebars;
|
||||
|
||||
/* istanbul ignore next */
|
||||
Handlebars.noConflict = function() {
|
||||
if (root.Handlebars === Handlebars) {
|
||||
root.Handlebars = $Handlebars;
|
||||
if (globalThis.Handlebars === Handlebars) {
|
||||
globalThis.Handlebars = $Handlebars;
|
||||
}
|
||||
return Handlebars;
|
||||
};
|
||||
|
||||
@@ -124,7 +124,7 @@ export function template(templateSpec, env) {
|
||||
loc: loc
|
||||
});
|
||||
}
|
||||
return obj[name];
|
||||
return container.lookupProperty(obj, name);
|
||||
},
|
||||
lookupProperty: function(parent, propertyName) {
|
||||
let result = parent[propertyName];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// USAGE:
|
||||
// var handlebars = require('handlebars');
|
||||
/* eslint-env node */
|
||||
/* eslint-disable no-var */
|
||||
|
||||
// var local = handlebars.create();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-env node */
|
||||
/* eslint-disable no-console */
|
||||
import Async from 'neo-async';
|
||||
import fs from 'fs';
|
||||
|
||||
Generated
+16979
-912
File diff suppressed because it is too large
Load Diff
+24
-22
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "handlebars",
|
||||
"barename": "handlebars",
|
||||
"version": "4.7.4",
|
||||
"version": "4.7.8",
|
||||
"description": "Handlebars provides the power necessary to let you build semantic templates effectively with no frustration",
|
||||
"homepage": "http://www.handlebarsjs.com/",
|
||||
"homepage": "https://handlebarsjs.com/",
|
||||
"keywords": [
|
||||
"handlebars",
|
||||
"mustache",
|
||||
@@ -12,7 +12,7 @@
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/wycats/handlebars.js.git"
|
||||
"url": "https://github.com/handlebars-lang/handlebars.js.git"
|
||||
},
|
||||
"author": "Yehuda Katz",
|
||||
"license": "MIT",
|
||||
@@ -21,21 +21,21 @@
|
||||
"node": ">=0.4.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"neo-async": "^2.6.0",
|
||||
"minimist": "^1.2.5",
|
||||
"neo-async": "^2.6.2",
|
||||
"source-map": "^0.6.1",
|
||||
"yargs": "^15.3.1"
|
||||
"wordwrap": "^1.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"uglify-js": "^3.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@knappi/grunt-saucelabs": "^9.0.2",
|
||||
"@playwright/test": "^1.17.1",
|
||||
"aws-sdk": "^2.1.49",
|
||||
"babel-loader": "^5.0.0",
|
||||
"babel-runtime": "^5.1.10",
|
||||
"benchmark": "~1.0",
|
||||
"chai": "^4.2.0",
|
||||
"chai-diff": "^1.0.1",
|
||||
"concurrently": "^5.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"dtslint": "^0.5.5",
|
||||
@@ -43,12 +43,11 @@
|
||||
"eco": "~1.1.0-rc-3",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-prettier": "^6.7.0",
|
||||
"eslint-plugin-compat": "^3.3.0",
|
||||
"eslint-plugin-compat": "^3.13.0",
|
||||
"eslint-plugin-es5": "^1.4.1",
|
||||
"fs-extra": "^8.1.0",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt-babel": "^5.0.0",
|
||||
"grunt-bg-shell": "^2.3.3",
|
||||
"grunt-cli": "^1",
|
||||
"grunt-contrib-clean": "^1",
|
||||
"grunt-contrib-concat": "^1",
|
||||
@@ -57,6 +56,7 @@
|
||||
"grunt-contrib-requirejs": "^1",
|
||||
"grunt-contrib-uglify": "^1",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-shell": "^4.0.0",
|
||||
"grunt-webpack": "^1.0.8",
|
||||
"husky": "^3.1.0",
|
||||
"jison": "~0.3.0",
|
||||
@@ -75,23 +75,26 @@
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"browser": {
|
||||
".": "./dist/cjs/handlebars.js",
|
||||
"./runtime": "./dist/cjs/handlebars.runtime.js"
|
||||
},
|
||||
"browser": "./dist/cjs/handlebars.js",
|
||||
"bin": {
|
||||
"handlebars": "bin/handlebars"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "grunt build",
|
||||
"release": "npm run build && grunt release",
|
||||
"format": "prettier --write '**/*.js' && eslint --fix .",
|
||||
"check-format": "prettier --check '**/*.js'",
|
||||
"lint": "eslint --max-warnings 0 .",
|
||||
"dtslint": "dtslint types",
|
||||
"test": "grunt",
|
||||
"extensive-tests-and-publish-to-aws": "npx mocha tasks/task-tests/ && grunt --stack extensive-tests-and-publish-to-aws",
|
||||
"integration-test": "grunt integration-tests",
|
||||
"lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types",
|
||||
"lint:eslint": "eslint --max-warnings 0 .",
|
||||
"lint:prettier": "prettier --check '**/*.js'",
|
||||
"lint:types": "dtslint types",
|
||||
"test": "npm run test:mocha",
|
||||
"test:mocha": "grunt build && grunt test",
|
||||
"test:browser": "playwright test --config tests/browser/playwright.config.js tests/browser/spec.js",
|
||||
"test:integration": "grunt integration-tests",
|
||||
"test:serve": "grunt connect:server:keepalive",
|
||||
"extensive-tests-and-publish-to-aws": "npx mocha tasks/tests/ && grunt --stack extensive-tests-and-publish-to-aws",
|
||||
"--- combined tasks ---": "",
|
||||
"check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:dtslint npm:check-format npm:test"
|
||||
"check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:test"
|
||||
},
|
||||
"jspm": {
|
||||
"main": "handlebars",
|
||||
@@ -108,7 +111,6 @@
|
||||
"dist/amd/**/*.js",
|
||||
"dist/cjs/**/*.js",
|
||||
"lib",
|
||||
"print-script",
|
||||
"release-notes.md",
|
||||
"runtime.js",
|
||||
"types/*.d.ts",
|
||||
@@ -120,7 +122,7 @@
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,css,json,md}": [
|
||||
"*.{js,css,json}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
],
|
||||
|
||||
+55
-1
@@ -2,7 +2,61 @@
|
||||
|
||||
## Development
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.7.4...master)
|
||||
[Commits](https://github.com/handlebars-lang/handlebars.js/compare/v4.7.8...master)
|
||||
|
||||
## v4.7.8 - July 27th, 2023
|
||||
|
||||
- Make library compatible with workers (#1894) - 3d3796c
|
||||
- Don't rely on Node.js global object (#1776) - 2954e7e
|
||||
- Fix compiling of each block params in strict mode (#1855) - 30dbf04
|
||||
- Fix rollup warning when importing Handlebars as ESM - 03d387b
|
||||
- Fix bundler issue with webpack 5 (#1862) - c6c6bbb
|
||||
- Use https instead of git for mustache submodule - 88ac068
|
||||
|
||||
[Commits](https://github.com/handlebars-lang/handlebars.js/compare/v4.7.7...v4.7.8)
|
||||
|
||||
## v4.7.7 - February 15th, 2021
|
||||
|
||||
- fix weird error in integration tests - eb860c0
|
||||
- fix: check prototype property access in strict-mode (#1736) - b6d3de7
|
||||
- fix: escape property names in compat mode (#1736) - f058970
|
||||
- refactor: In spec tests, use expectTemplate over equals and shouldThrow (#1683) - 77825f8
|
||||
- chore: start testing on Node.js 12 and 13 - 3789a30
|
||||
|
||||
(POSSIBLY) BREAKING CHANGES:
|
||||
|
||||
- the changes from version [4.6.0](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md#v460---january-8th-2020) now also apply
|
||||
in when using the compile-option "strict: true". Access to prototype properties is forbidden completely by default, specific properties or methods
|
||||
can be allowed via runtime-options. See #1633 for details. If you are using Handlebars as documented, you should not be accessing prototype properties
|
||||
from your template anyway, so the changes should not be a problem for you. Only the use of undocumented features can break your build.
|
||||
|
||||
That is why we only bump the patch version despite mentioning breaking changes.
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.7.6...v4.7.7)
|
||||
|
||||
## v4.7.6 - April 3rd, 2020
|
||||
|
||||
Chore/Housekeeping:
|
||||
|
||||
- [#1672](https://github.com/wycats/handlebars.js/issues/1672) - Switch cmd parser to latest minimist ([@dougwilson](https://api.github.com/users/dougwilson)
|
||||
|
||||
Compatibility notes:
|
||||
|
||||
- Restored Node.js compatibility
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.7.5...v4.7.6)
|
||||
|
||||
## v4.7.5 - April 2nd, 2020
|
||||
|
||||
Chore/Housekeeping:
|
||||
|
||||
- ~Node.js version support has been changed to v6+~ Reverted in 4.7.6
|
||||
|
||||
Compatibility notes:
|
||||
|
||||
- ~Node.js < v6 is no longer supported~ Reverted in 4.7.6
|
||||
|
||||
[Commits](https://github.com/wycats/handlebars.js/compare/v4.7.4...v4.7.5)
|
||||
|
||||
## v4.7.4 - April 1st, 2020
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
}
|
||||
var runner = mocha.run();
|
||||
|
||||
//Reporting for saucelabs
|
||||
// Reporting to test-runner
|
||||
var failedTests = [];
|
||||
runner.on('end', function(){
|
||||
window.mochaResults = runner.stats;
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@
|
||||
}
|
||||
var runner = mocha.run();
|
||||
|
||||
//Reporting for saucelabs
|
||||
// Reporting to test-runner
|
||||
var failedTests = [];
|
||||
runner.on('end', function(){
|
||||
window.mochaResults = runner.stats;
|
||||
|
||||
+387
-399
@@ -6,117 +6,156 @@ beforeEach(function() {
|
||||
|
||||
describe('basic context', function() {
|
||||
it('most basic', function() {
|
||||
shouldCompileTo('{{foo}}', { foo: 'foo' }, 'foo');
|
||||
expectTemplate('{{foo}}')
|
||||
.withInput({ foo: 'foo' })
|
||||
.toCompileTo('foo');
|
||||
});
|
||||
|
||||
it('escaping', function() {
|
||||
shouldCompileTo('\\{{foo}}', { foo: 'food' }, '{{foo}}');
|
||||
shouldCompileTo('content \\{{foo}}', { foo: 'food' }, 'content {{foo}}');
|
||||
shouldCompileTo('\\\\{{foo}}', { foo: 'food' }, '\\food');
|
||||
shouldCompileTo('content \\\\{{foo}}', { foo: 'food' }, 'content \\food');
|
||||
shouldCompileTo('\\\\ {{foo}}', { foo: 'food' }, '\\\\ food');
|
||||
expectTemplate('\\{{foo}}')
|
||||
.withInput({ foo: 'food' })
|
||||
.toCompileTo('{{foo}}');
|
||||
|
||||
expectTemplate('content \\{{foo}}')
|
||||
.withInput({ foo: 'food' })
|
||||
.toCompileTo('content {{foo}}');
|
||||
|
||||
expectTemplate('\\\\{{foo}}')
|
||||
.withInput({ foo: 'food' })
|
||||
.toCompileTo('\\food');
|
||||
|
||||
expectTemplate('content \\\\{{foo}}')
|
||||
.withInput({ foo: 'food' })
|
||||
.toCompileTo('content \\food');
|
||||
|
||||
expectTemplate('\\\\ {{foo}}')
|
||||
.withInput({ foo: 'food' })
|
||||
.toCompileTo('\\\\ food');
|
||||
});
|
||||
|
||||
it('compiling with a basic context', function() {
|
||||
shouldCompileTo(
|
||||
'Goodbye\n{{cruel}}\n{{world}}!',
|
||||
{ cruel: 'cruel', world: 'world' },
|
||||
'Goodbye\ncruel\nworld!',
|
||||
'It works if all the required keys are provided'
|
||||
);
|
||||
expectTemplate('Goodbye\n{{cruel}}\n{{world}}!')
|
||||
.withInput({
|
||||
cruel: 'cruel',
|
||||
world: 'world'
|
||||
})
|
||||
.withMessage('It works if all the required keys are provided')
|
||||
.toCompileTo('Goodbye\ncruel\nworld!');
|
||||
});
|
||||
|
||||
it('compiling with a string context', function() {
|
||||
shouldCompileTo('{{.}}{{length}}', 'bye', 'bye3');
|
||||
expectTemplate('{{.}}{{length}}')
|
||||
.withInput('bye')
|
||||
.toCompileTo('bye3');
|
||||
});
|
||||
|
||||
it('compiling with an undefined context', function() {
|
||||
shouldCompileTo(
|
||||
'Goodbye\n{{cruel}}\n{{world.bar}}!',
|
||||
undefined,
|
||||
'Goodbye\n\n!'
|
||||
);
|
||||
expectTemplate('Goodbye\n{{cruel}}\n{{world.bar}}!')
|
||||
.withInput(undefined)
|
||||
.toCompileTo('Goodbye\n\n!');
|
||||
|
||||
shouldCompileTo(
|
||||
'{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}',
|
||||
undefined,
|
||||
'Goodbye'
|
||||
);
|
||||
expectTemplate('{{#unless foo}}Goodbye{{../test}}{{test2}}{{/unless}}')
|
||||
.withInput(undefined)
|
||||
.toCompileTo('Goodbye');
|
||||
});
|
||||
|
||||
it('comments', function() {
|
||||
shouldCompileTo(
|
||||
'{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!',
|
||||
{ cruel: 'cruel', world: 'world' },
|
||||
'Goodbye\ncruel\nworld!',
|
||||
'comments are ignored'
|
||||
expectTemplate('{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!')
|
||||
.withInput({
|
||||
cruel: 'cruel',
|
||||
world: 'world'
|
||||
})
|
||||
.withMessage('comments are ignored')
|
||||
.toCompileTo('Goodbye\ncruel\nworld!');
|
||||
|
||||
expectTemplate(' {{~! comment ~}} blah').toCompileTo('blah');
|
||||
|
||||
expectTemplate(' {{~!-- long-comment --~}} blah').toCompileTo(
|
||||
'blah'
|
||||
);
|
||||
|
||||
shouldCompileTo(' {{~! comment ~}} blah', {}, 'blah');
|
||||
shouldCompileTo(' {{~!-- long-comment --~}} blah', {}, 'blah');
|
||||
shouldCompileTo(' {{! comment ~}} blah', {}, ' blah');
|
||||
shouldCompileTo(' {{!-- long-comment --~}} blah', {}, ' blah');
|
||||
shouldCompileTo(' {{~! comment}} blah', {}, ' blah');
|
||||
shouldCompileTo(' {{~!-- long-comment --}} blah', {}, ' blah');
|
||||
expectTemplate(' {{! comment ~}} blah').toCompileTo(' blah');
|
||||
|
||||
expectTemplate(' {{!-- long-comment --~}} blah').toCompileTo(
|
||||
' blah'
|
||||
);
|
||||
|
||||
expectTemplate(' {{~! comment}} blah').toCompileTo(' blah');
|
||||
|
||||
expectTemplate(' {{~!-- long-comment --}} blah').toCompileTo(
|
||||
' blah'
|
||||
);
|
||||
});
|
||||
|
||||
it('boolean', function() {
|
||||
var string = '{{#goodbye}}GOODBYE {{/goodbye}}cruel {{world}}!';
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ goodbye: true, world: 'world' },
|
||||
'GOODBYE cruel world!',
|
||||
'booleans show the contents when true'
|
||||
);
|
||||
expectTemplate(string)
|
||||
.withInput({
|
||||
goodbye: true,
|
||||
world: 'world'
|
||||
})
|
||||
.withMessage('booleans show the contents when true')
|
||||
.toCompileTo('GOODBYE cruel world!');
|
||||
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ goodbye: false, world: 'world' },
|
||||
'cruel world!',
|
||||
'booleans do not show the contents when false'
|
||||
);
|
||||
expectTemplate(string)
|
||||
.withInput({
|
||||
goodbye: false,
|
||||
world: 'world'
|
||||
})
|
||||
.withMessage('booleans do not show the contents when false')
|
||||
.toCompileTo('cruel world!');
|
||||
});
|
||||
|
||||
it('zeros', function() {
|
||||
shouldCompileTo(
|
||||
'num1: {{num1}}, num2: {{num2}}',
|
||||
{ num1: 42, num2: 0 },
|
||||
'num1: 42, num2: 0'
|
||||
);
|
||||
shouldCompileTo('num: {{.}}', 0, 'num: 0');
|
||||
shouldCompileTo('num: {{num1/num2}}', { num1: { num2: 0 } }, 'num: 0');
|
||||
expectTemplate('num1: {{num1}}, num2: {{num2}}')
|
||||
.withInput({
|
||||
num1: 42,
|
||||
num2: 0
|
||||
})
|
||||
.toCompileTo('num1: 42, num2: 0');
|
||||
|
||||
expectTemplate('num: {{.}}')
|
||||
.withInput(0)
|
||||
.toCompileTo('num: 0');
|
||||
|
||||
expectTemplate('num: {{num1/num2}}')
|
||||
.withInput({ num1: { num2: 0 } })
|
||||
.toCompileTo('num: 0');
|
||||
});
|
||||
|
||||
it('false', function() {
|
||||
/* eslint-disable no-new-wrappers */
|
||||
shouldCompileTo(
|
||||
'val1: {{val1}}, val2: {{val2}}',
|
||||
{ val1: false, val2: new Boolean(false) },
|
||||
'val1: false, val2: false'
|
||||
);
|
||||
shouldCompileTo('val: {{.}}', false, 'val: false');
|
||||
shouldCompileTo(
|
||||
'val: {{val1/val2}}',
|
||||
{ val1: { val2: false } },
|
||||
'val: false'
|
||||
);
|
||||
expectTemplate('val1: {{val1}}, val2: {{val2}}')
|
||||
.withInput({
|
||||
val1: false,
|
||||
val2: new Boolean(false)
|
||||
})
|
||||
.toCompileTo('val1: false, val2: false');
|
||||
|
||||
shouldCompileTo(
|
||||
'val1: {{{val1}}}, val2: {{{val2}}}',
|
||||
{ val1: false, val2: new Boolean(false) },
|
||||
'val1: false, val2: false'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'val: {{{val1/val2}}}',
|
||||
{ val1: { val2: false } },
|
||||
'val: false'
|
||||
);
|
||||
expectTemplate('val: {{.}}')
|
||||
.withInput(false)
|
||||
.toCompileTo('val: false');
|
||||
|
||||
expectTemplate('val: {{val1/val2}}')
|
||||
.withInput({ val1: { val2: false } })
|
||||
.toCompileTo('val: false');
|
||||
|
||||
expectTemplate('val1: {{{val1}}}, val2: {{{val2}}}')
|
||||
.withInput({
|
||||
val1: false,
|
||||
val2: new Boolean(false)
|
||||
})
|
||||
.toCompileTo('val1: false, val2: false');
|
||||
|
||||
expectTemplate('val: {{{val1/val2}}}')
|
||||
.withInput({ val1: { val2: false } })
|
||||
.toCompileTo('val: false');
|
||||
/* eslint-enable */
|
||||
});
|
||||
|
||||
it('should handle undefined and null', function() {
|
||||
shouldCompileTo(
|
||||
'{{awesome undefined null}}',
|
||||
{
|
||||
expectTemplate('{{awesome undefined null}}')
|
||||
.withInput({
|
||||
awesome: function(_undefined, _null, options) {
|
||||
return (
|
||||
(_undefined === undefined) +
|
||||
@@ -126,373 +165,325 @@ describe('basic context', function() {
|
||||
typeof options
|
||||
);
|
||||
}
|
||||
},
|
||||
'true true object'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{undefined}}',
|
||||
{
|
||||
})
|
||||
.toCompileTo('true true object');
|
||||
|
||||
expectTemplate('{{undefined}}')
|
||||
.withInput({
|
||||
undefined: function() {
|
||||
return 'undefined!';
|
||||
}
|
||||
},
|
||||
'undefined!'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{null}}',
|
||||
{
|
||||
})
|
||||
.toCompileTo('undefined!');
|
||||
|
||||
expectTemplate('{{null}}')
|
||||
.withInput({
|
||||
null: function() {
|
||||
return 'null!';
|
||||
}
|
||||
},
|
||||
'null!'
|
||||
);
|
||||
})
|
||||
.toCompileTo('null!');
|
||||
});
|
||||
|
||||
it('newlines', function() {
|
||||
shouldCompileTo("Alan's\nTest", {}, "Alan's\nTest");
|
||||
shouldCompileTo("Alan's\rTest", {}, "Alan's\rTest");
|
||||
expectTemplate("Alan's\nTest").toCompileTo("Alan's\nTest");
|
||||
|
||||
expectTemplate("Alan's\rTest").toCompileTo("Alan's\rTest");
|
||||
});
|
||||
|
||||
it('escaping text', function() {
|
||||
shouldCompileTo(
|
||||
"Awesome's",
|
||||
{},
|
||||
"Awesome's",
|
||||
"text is escaped so that it doesn't get caught on single quotes"
|
||||
);
|
||||
shouldCompileTo(
|
||||
'Awesome\\',
|
||||
{},
|
||||
'Awesome\\',
|
||||
"text is escaped so that the closing quote can't be ignored"
|
||||
);
|
||||
shouldCompileTo(
|
||||
'Awesome\\\\ foo',
|
||||
{},
|
||||
'Awesome\\\\ foo',
|
||||
"text is escaped so that it doesn't mess up backslashes"
|
||||
);
|
||||
shouldCompileTo(
|
||||
'Awesome {{foo}}',
|
||||
{ foo: '\\' },
|
||||
'Awesome \\',
|
||||
"text is escaped so that it doesn't mess up backslashes"
|
||||
);
|
||||
shouldCompileTo(
|
||||
" ' ' ",
|
||||
{},
|
||||
" ' ' ",
|
||||
'double quotes never produce invalid javascript'
|
||||
);
|
||||
expectTemplate("Awesome's")
|
||||
.withMessage(
|
||||
"text is escaped so that it doesn't get caught on single quotes"
|
||||
)
|
||||
.toCompileTo("Awesome's");
|
||||
|
||||
expectTemplate('Awesome\\')
|
||||
.withMessage("text is escaped so that the closing quote can't be ignored")
|
||||
.toCompileTo('Awesome\\');
|
||||
|
||||
expectTemplate('Awesome\\\\ foo')
|
||||
.withMessage("text is escaped so that it doesn't mess up backslashes")
|
||||
.toCompileTo('Awesome\\\\ foo');
|
||||
|
||||
expectTemplate('Awesome {{foo}}')
|
||||
.withInput({ foo: '\\' })
|
||||
.withMessage("text is escaped so that it doesn't mess up backslashes")
|
||||
.toCompileTo('Awesome \\');
|
||||
|
||||
expectTemplate(" ' ' ")
|
||||
.withMessage('double quotes never produce invalid javascript')
|
||||
.toCompileTo(" ' ' ");
|
||||
});
|
||||
|
||||
it('escaping expressions', function() {
|
||||
shouldCompileTo(
|
||||
'{{{awesome}}}',
|
||||
{ awesome: "&'\\<>" },
|
||||
"&'\\<>",
|
||||
"expressions with 3 handlebars aren't escaped"
|
||||
);
|
||||
expectTemplate('{{{awesome}}}')
|
||||
.withInput({ awesome: "&'\\<>" })
|
||||
.withMessage("expressions with 3 handlebars aren't escaped")
|
||||
.toCompileTo("&'\\<>");
|
||||
|
||||
shouldCompileTo(
|
||||
'{{&awesome}}',
|
||||
{ awesome: "&'\\<>" },
|
||||
"&'\\<>",
|
||||
"expressions with {{& handlebars aren't escaped"
|
||||
);
|
||||
expectTemplate('{{&awesome}}')
|
||||
.withInput({ awesome: "&'\\<>" })
|
||||
.withMessage("expressions with {{& handlebars aren't escaped")
|
||||
.toCompileTo("&'\\<>");
|
||||
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
{ awesome: '&"\'`\\<>' },
|
||||
'&"'`\\<>',
|
||||
'by default expressions should be escaped'
|
||||
);
|
||||
expectTemplate('{{awesome}}')
|
||||
.withInput({ awesome: '&"\'`\\<>' })
|
||||
.withMessage('by default expressions should be escaped')
|
||||
.toCompileTo('&"'`\\<>');
|
||||
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
{ awesome: 'Escaped, <b> looks like: <b>' },
|
||||
'Escaped, <b> looks like: &lt;b&gt;',
|
||||
'escaping should properly handle amperstands'
|
||||
);
|
||||
expectTemplate('{{awesome}}')
|
||||
.withInput({ awesome: 'Escaped, <b> looks like: <b>' })
|
||||
.withMessage('escaping should properly handle amperstands')
|
||||
.toCompileTo('Escaped, <b> looks like: &lt;b&gt;');
|
||||
});
|
||||
|
||||
it("functions returning safestrings shouldn't be escaped", function() {
|
||||
var hash = {
|
||||
awesome: function() {
|
||||
return new Handlebars.SafeString("&'\\<>");
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
hash,
|
||||
"&'\\<>",
|
||||
"functions returning safestrings aren't escaped"
|
||||
);
|
||||
expectTemplate('{{awesome}}')
|
||||
.withInput({
|
||||
awesome: function() {
|
||||
return new Handlebars.SafeString("&'\\<>");
|
||||
}
|
||||
})
|
||||
.withMessage("functions returning safestrings aren't escaped")
|
||||
.toCompileTo("&'\\<>");
|
||||
});
|
||||
|
||||
it('functions', function() {
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
{
|
||||
expectTemplate('{{awesome}}')
|
||||
.withInput({
|
||||
awesome: function() {
|
||||
return 'Awesome';
|
||||
}
|
||||
},
|
||||
'Awesome',
|
||||
'functions are called and render their output'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{awesome}}',
|
||||
{
|
||||
})
|
||||
.withMessage('functions are called and render their output')
|
||||
.toCompileTo('Awesome');
|
||||
|
||||
expectTemplate('{{awesome}}')
|
||||
.withInput({
|
||||
awesome: function() {
|
||||
return this.more;
|
||||
},
|
||||
more: 'More awesome'
|
||||
},
|
||||
'More awesome',
|
||||
'functions are bound to the context'
|
||||
);
|
||||
})
|
||||
.withMessage('functions are bound to the context')
|
||||
.toCompileTo('More awesome');
|
||||
});
|
||||
|
||||
it('functions with context argument', function() {
|
||||
shouldCompileTo(
|
||||
'{{awesome frank}}',
|
||||
{
|
||||
expectTemplate('{{awesome frank}}')
|
||||
.withInput({
|
||||
awesome: function(context) {
|
||||
return context;
|
||||
},
|
||||
frank: 'Frank'
|
||||
},
|
||||
'Frank',
|
||||
'functions are called with context arguments'
|
||||
);
|
||||
})
|
||||
.withMessage('functions are called with context arguments')
|
||||
.toCompileTo('Frank');
|
||||
});
|
||||
|
||||
it('pathed functions with context argument', function() {
|
||||
shouldCompileTo(
|
||||
'{{bar.awesome frank}}',
|
||||
{
|
||||
expectTemplate('{{bar.awesome frank}}')
|
||||
.withInput({
|
||||
bar: {
|
||||
awesome: function(context) {
|
||||
return context;
|
||||
}
|
||||
},
|
||||
frank: 'Frank'
|
||||
},
|
||||
'Frank',
|
||||
'functions are called with context arguments'
|
||||
);
|
||||
})
|
||||
.withMessage('functions are called with context arguments')
|
||||
.toCompileTo('Frank');
|
||||
});
|
||||
|
||||
it('depthed functions with context argument', function() {
|
||||
shouldCompileTo(
|
||||
'{{#with frank}}{{../awesome .}}{{/with}}',
|
||||
{
|
||||
expectTemplate('{{#with frank}}{{../awesome .}}{{/with}}')
|
||||
.withInput({
|
||||
awesome: function(context) {
|
||||
return context;
|
||||
},
|
||||
frank: 'Frank'
|
||||
},
|
||||
'Frank',
|
||||
'functions are called with context arguments'
|
||||
);
|
||||
})
|
||||
.withMessage('functions are called with context arguments')
|
||||
.toCompileTo('Frank');
|
||||
});
|
||||
|
||||
it('block functions with context argument', function() {
|
||||
shouldCompileTo(
|
||||
'{{#awesome 1}}inner {{.}}{{/awesome}}',
|
||||
{
|
||||
expectTemplate('{{#awesome 1}}inner {{.}}{{/awesome}}')
|
||||
.withInput({
|
||||
awesome: function(context, options) {
|
||||
return options.fn(context);
|
||||
}
|
||||
},
|
||||
'inner 1',
|
||||
'block functions are called with context and options'
|
||||
);
|
||||
})
|
||||
.withMessage('block functions are called with context and options')
|
||||
.toCompileTo('inner 1');
|
||||
});
|
||||
|
||||
it('depthed block functions with context argument', function() {
|
||||
shouldCompileTo(
|
||||
'{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}',
|
||||
{
|
||||
expectTemplate(
|
||||
'{{#with value}}{{#../awesome 1}}inner {{.}}{{/../awesome}}{{/with}}'
|
||||
)
|
||||
.withInput({
|
||||
value: true,
|
||||
awesome: function(context, options) {
|
||||
return options.fn(context);
|
||||
}
|
||||
},
|
||||
'inner 1',
|
||||
'block functions are called with context and options'
|
||||
);
|
||||
})
|
||||
.withMessage('block functions are called with context and options')
|
||||
.toCompileTo('inner 1');
|
||||
});
|
||||
|
||||
it('block functions without context argument', function() {
|
||||
shouldCompileTo(
|
||||
'{{#awesome}}inner{{/awesome}}',
|
||||
{
|
||||
expectTemplate('{{#awesome}}inner{{/awesome}}')
|
||||
.withInput({
|
||||
awesome: function(options) {
|
||||
return options.fn(this);
|
||||
}
|
||||
},
|
||||
'inner',
|
||||
'block functions are called with options'
|
||||
);
|
||||
})
|
||||
.withMessage('block functions are called with options')
|
||||
.toCompileTo('inner');
|
||||
});
|
||||
|
||||
it('pathed block functions without context argument', function() {
|
||||
shouldCompileTo(
|
||||
'{{#foo.awesome}}inner{{/foo.awesome}}',
|
||||
{
|
||||
expectTemplate('{{#foo.awesome}}inner{{/foo.awesome}}')
|
||||
.withInput({
|
||||
foo: {
|
||||
awesome: function() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
},
|
||||
'inner',
|
||||
'block functions are called with options'
|
||||
);
|
||||
})
|
||||
.withMessage('block functions are called with options')
|
||||
.toCompileTo('inner');
|
||||
});
|
||||
|
||||
it('depthed block functions without context argument', function() {
|
||||
shouldCompileTo(
|
||||
'{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}',
|
||||
{
|
||||
expectTemplate(
|
||||
'{{#with value}}{{#../awesome}}inner{{/../awesome}}{{/with}}'
|
||||
)
|
||||
.withInput({
|
||||
value: true,
|
||||
awesome: function() {
|
||||
return this;
|
||||
}
|
||||
},
|
||||
'inner',
|
||||
'block functions are called with options'
|
||||
);
|
||||
})
|
||||
.withMessage('block functions are called with options')
|
||||
.toCompileTo('inner');
|
||||
});
|
||||
|
||||
it('paths with hyphens', function() {
|
||||
shouldCompileTo(
|
||||
'{{foo-bar}}',
|
||||
{ 'foo-bar': 'baz' },
|
||||
'baz',
|
||||
'Paths can contain hyphens (-)'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{foo.foo-bar}}',
|
||||
{ foo: { 'foo-bar': 'baz' } },
|
||||
'baz',
|
||||
'Paths can contain hyphens (-)'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{foo/foo-bar}}',
|
||||
{ foo: { 'foo-bar': 'baz' } },
|
||||
'baz',
|
||||
'Paths can contain hyphens (-)'
|
||||
);
|
||||
expectTemplate('{{foo-bar}}')
|
||||
.withInput({ 'foo-bar': 'baz' })
|
||||
.withMessage('Paths can contain hyphens (-)')
|
||||
.toCompileTo('baz');
|
||||
|
||||
expectTemplate('{{foo.foo-bar}}')
|
||||
.withInput({ foo: { 'foo-bar': 'baz' } })
|
||||
.withMessage('Paths can contain hyphens (-)')
|
||||
.toCompileTo('baz');
|
||||
|
||||
expectTemplate('{{foo/foo-bar}}')
|
||||
.withInput({ foo: { 'foo-bar': 'baz' } })
|
||||
.withMessage('Paths can contain hyphens (-)')
|
||||
.toCompileTo('baz');
|
||||
});
|
||||
|
||||
it('nested paths', function() {
|
||||
shouldCompileTo(
|
||||
'Goodbye {{alan/expression}} world!',
|
||||
{ alan: { expression: 'beautiful' } },
|
||||
'Goodbye beautiful world!',
|
||||
'Nested paths access nested objects'
|
||||
);
|
||||
expectTemplate('Goodbye {{alan/expression}} world!')
|
||||
.withInput({ alan: { expression: 'beautiful' } })
|
||||
.withMessage('Nested paths access nested objects')
|
||||
.toCompileTo('Goodbye beautiful world!');
|
||||
});
|
||||
|
||||
it('nested paths with empty string value', function() {
|
||||
shouldCompileTo(
|
||||
'Goodbye {{alan/expression}} world!',
|
||||
{ alan: { expression: '' } },
|
||||
'Goodbye world!',
|
||||
'Nested paths access nested objects with empty string'
|
||||
);
|
||||
expectTemplate('Goodbye {{alan/expression}} world!')
|
||||
.withInput({ alan: { expression: '' } })
|
||||
.withMessage('Nested paths access nested objects with empty string')
|
||||
.toCompileTo('Goodbye world!');
|
||||
});
|
||||
|
||||
it('literal paths', function() {
|
||||
shouldCompileTo(
|
||||
'Goodbye {{[@alan]/expression}} world!',
|
||||
{ '@alan': { expression: 'beautiful' } },
|
||||
'Goodbye beautiful world!',
|
||||
'Literal paths can be used'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'Goodbye {{[foo bar]/expression}} world!',
|
||||
{ 'foo bar': { expression: 'beautiful' } },
|
||||
'Goodbye beautiful world!',
|
||||
'Literal paths can be used'
|
||||
);
|
||||
expectTemplate('Goodbye {{[@alan]/expression}} world!')
|
||||
.withInput({ '@alan': { expression: 'beautiful' } })
|
||||
.withMessage('Literal paths can be used')
|
||||
.toCompileTo('Goodbye beautiful world!');
|
||||
|
||||
expectTemplate('Goodbye {{[foo bar]/expression}} world!')
|
||||
.withInput({ 'foo bar': { expression: 'beautiful' } })
|
||||
.withMessage('Literal paths can be used')
|
||||
.toCompileTo('Goodbye beautiful world!');
|
||||
});
|
||||
|
||||
it('literal references', function() {
|
||||
shouldCompileTo(
|
||||
'Goodbye {{[foo bar]}} world!',
|
||||
{ 'foo bar': 'beautiful' },
|
||||
'Goodbye beautiful world!'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'Goodbye {{"foo bar"}} world!',
|
||||
{ 'foo bar': 'beautiful' },
|
||||
'Goodbye beautiful world!'
|
||||
);
|
||||
shouldCompileTo(
|
||||
"Goodbye {{'foo bar'}} world!",
|
||||
{ 'foo bar': 'beautiful' },
|
||||
'Goodbye beautiful world!'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'Goodbye {{"foo[bar"}} world!',
|
||||
{ 'foo[bar': 'beautiful' },
|
||||
'Goodbye beautiful world!'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'Goodbye {{"foo\'bar"}} world!',
|
||||
{ "foo'bar": 'beautiful' },
|
||||
'Goodbye beautiful world!'
|
||||
);
|
||||
shouldCompileTo(
|
||||
"Goodbye {{'foo\"bar'}} world!",
|
||||
{ 'foo"bar': 'beautiful' },
|
||||
'Goodbye beautiful world!'
|
||||
);
|
||||
expectTemplate('Goodbye {{[foo bar]}} world!')
|
||||
.withInput({ 'foo bar': 'beautiful' })
|
||||
.toCompileTo('Goodbye beautiful world!');
|
||||
|
||||
expectTemplate('Goodbye {{"foo bar"}} world!')
|
||||
.withInput({ 'foo bar': 'beautiful' })
|
||||
.toCompileTo('Goodbye beautiful world!');
|
||||
|
||||
expectTemplate("Goodbye {{'foo bar'}} world!")
|
||||
.withInput({ 'foo bar': 'beautiful' })
|
||||
.toCompileTo('Goodbye beautiful world!');
|
||||
|
||||
expectTemplate('Goodbye {{"foo[bar"}} world!')
|
||||
.withInput({ 'foo[bar': 'beautiful' })
|
||||
.toCompileTo('Goodbye beautiful world!');
|
||||
|
||||
expectTemplate('Goodbye {{"foo\'bar"}} world!')
|
||||
.withInput({ "foo'bar": 'beautiful' })
|
||||
.toCompileTo('Goodbye beautiful world!');
|
||||
|
||||
expectTemplate("Goodbye {{'foo\"bar'}} world!")
|
||||
.withInput({ 'foo"bar': 'beautiful' })
|
||||
.toCompileTo('Goodbye beautiful world!');
|
||||
});
|
||||
|
||||
it("that current context path ({{.}}) doesn't hit helpers", function() {
|
||||
shouldCompileTo('test: {{.}}', [null, { helper: 'awesome' }], 'test: ');
|
||||
expectTemplate('test: {{.}}')
|
||||
.withInput(null)
|
||||
.withHelpers({ helper: 'awesome' })
|
||||
.toCompileTo('test: ');
|
||||
});
|
||||
|
||||
it('complex but empty paths', function() {
|
||||
shouldCompileTo('{{person/name}}', { person: { name: null } }, '');
|
||||
shouldCompileTo('{{person/name}}', { person: {} }, '');
|
||||
expectTemplate('{{person/name}}')
|
||||
.withInput({ person: { name: null } })
|
||||
.toCompileTo('');
|
||||
|
||||
expectTemplate('{{person/name}}')
|
||||
.withInput({ person: {} })
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('this keyword in paths', function() {
|
||||
var string = '{{#goodbyes}}{{this}}{{/goodbyes}}';
|
||||
var hash = { goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] };
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'goodbyeGoodbyeGOODBYE',
|
||||
'This keyword in paths evaluates to current context'
|
||||
);
|
||||
expectTemplate('{{#goodbyes}}{{this}}{{/goodbyes}}')
|
||||
.withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] })
|
||||
.withMessage('This keyword in paths evaluates to current context')
|
||||
.toCompileTo('goodbyeGoodbyeGOODBYE');
|
||||
|
||||
string = '{{#hellos}}{{this/text}}{{/hellos}}';
|
||||
hash = {
|
||||
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'helloHelloHELLO',
|
||||
'This keyword evaluates in more complex paths'
|
||||
);
|
||||
expectTemplate('{{#hellos}}{{this/text}}{{/hellos}}')
|
||||
.withInput({
|
||||
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
|
||||
})
|
||||
.withMessage('This keyword evaluates in more complex paths')
|
||||
.toCompileTo('helloHelloHELLO');
|
||||
});
|
||||
|
||||
it('this keyword nested inside path', function() {
|
||||
shouldThrow(
|
||||
function() {
|
||||
CompilerContext.compile('{{#hellos}}{{text/this/foo}}{{/hellos}}');
|
||||
},
|
||||
expectTemplate('{{#hellos}}{{text/this/foo}}{{/hellos}}').toThrow(
|
||||
Error,
|
||||
'Invalid path: text/this - 1:13'
|
||||
);
|
||||
|
||||
shouldCompileTo('{{[this]}}', { this: 'bar' }, 'bar');
|
||||
shouldCompileTo('{{text/[this]}}', { text: { this: 'bar' } }, 'bar');
|
||||
expectTemplate('{{[this]}}')
|
||||
.withInput({ this: 'bar' })
|
||||
.toCompileTo('bar');
|
||||
|
||||
expectTemplate('{{text/[this]}}')
|
||||
.withInput({ text: { this: 'bar' } })
|
||||
.toCompileTo('bar');
|
||||
});
|
||||
|
||||
it('this keyword in helpers', function() {
|
||||
@@ -501,108 +492,105 @@ describe('basic context', function() {
|
||||
return 'bar ' + value;
|
||||
}
|
||||
};
|
||||
var string = '{{#goodbyes}}{{foo this}}{{/goodbyes}}';
|
||||
var hash = { goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] };
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, helpers],
|
||||
'bar goodbyebar Goodbyebar GOODBYE',
|
||||
'This keyword in paths evaluates to current context'
|
||||
);
|
||||
|
||||
string = '{{#hellos}}{{foo this/text}}{{/hellos}}';
|
||||
hash = {
|
||||
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, helpers],
|
||||
'bar hellobar Hellobar HELLO',
|
||||
'This keyword evaluates in more complex paths'
|
||||
);
|
||||
expectTemplate('{{#goodbyes}}{{foo this}}{{/goodbyes}}')
|
||||
.withInput({ goodbyes: ['goodbye', 'Goodbye', 'GOODBYE'] })
|
||||
.withHelpers(helpers)
|
||||
.withMessage('This keyword in paths evaluates to current context')
|
||||
.toCompileTo('bar goodbyebar Goodbyebar GOODBYE');
|
||||
|
||||
expectTemplate('{{#hellos}}{{foo this/text}}{{/hellos}}')
|
||||
.withInput({
|
||||
hellos: [{ text: 'hello' }, { text: 'Hello' }, { text: 'HELLO' }]
|
||||
})
|
||||
.withHelpers(helpers)
|
||||
.withMessage('This keyword evaluates in more complex paths')
|
||||
.toCompileTo('bar hellobar Hellobar HELLO');
|
||||
});
|
||||
|
||||
it('this keyword nested inside helpers param', function() {
|
||||
var string = '{{#hellos}}{{foo text/this/foo}}{{/hellos}}';
|
||||
shouldThrow(
|
||||
function() {
|
||||
CompilerContext.compile(string);
|
||||
},
|
||||
expectTemplate('{{#hellos}}{{foo text/this/foo}}{{/hellos}}').toThrow(
|
||||
Error,
|
||||
'Invalid path: text/this - 1:17'
|
||||
);
|
||||
|
||||
shouldCompileTo(
|
||||
'{{foo [this]}}',
|
||||
{
|
||||
expectTemplate('{{foo [this]}}')
|
||||
.withInput({
|
||||
foo: function(value) {
|
||||
return value;
|
||||
},
|
||||
this: 'bar'
|
||||
},
|
||||
'bar'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{foo text/[this]}}',
|
||||
{
|
||||
})
|
||||
.toCompileTo('bar');
|
||||
|
||||
expectTemplate('{{foo text/[this]}}')
|
||||
.withInput({
|
||||
foo: function(value) {
|
||||
return value;
|
||||
},
|
||||
text: { this: 'bar' }
|
||||
},
|
||||
'bar'
|
||||
);
|
||||
})
|
||||
.toCompileTo('bar');
|
||||
});
|
||||
|
||||
it('pass string literals', function() {
|
||||
shouldCompileTo('{{"foo"}}', {}, '');
|
||||
shouldCompileTo('{{"foo"}}', { foo: 'bar' }, 'bar');
|
||||
shouldCompileTo(
|
||||
'{{#"foo"}}{{.}}{{/"foo"}}',
|
||||
{ foo: ['bar', 'baz'] },
|
||||
'barbaz'
|
||||
);
|
||||
expectTemplate('{{"foo"}}').toCompileTo('');
|
||||
|
||||
expectTemplate('{{"foo"}}')
|
||||
.withInput({ foo: 'bar' })
|
||||
.toCompileTo('bar');
|
||||
|
||||
expectTemplate('{{#"foo"}}{{.}}{{/"foo"}}')
|
||||
.withInput({
|
||||
foo: ['bar', 'baz']
|
||||
})
|
||||
.toCompileTo('barbaz');
|
||||
});
|
||||
|
||||
it('pass number literals', function() {
|
||||
shouldCompileTo('{{12}}', {}, '');
|
||||
shouldCompileTo('{{12}}', { '12': 'bar' }, 'bar');
|
||||
shouldCompileTo('{{12.34}}', {}, '');
|
||||
shouldCompileTo('{{12.34}}', { '12.34': 'bar' }, 'bar');
|
||||
shouldCompileTo(
|
||||
'{{12.34 1}}',
|
||||
{
|
||||
expectTemplate('{{12}}').toCompileTo('');
|
||||
|
||||
expectTemplate('{{12}}')
|
||||
.withInput({ '12': 'bar' })
|
||||
.toCompileTo('bar');
|
||||
|
||||
expectTemplate('{{12.34}}').toCompileTo('');
|
||||
|
||||
expectTemplate('{{12.34}}')
|
||||
.withInput({ '12.34': 'bar' })
|
||||
.toCompileTo('bar');
|
||||
|
||||
expectTemplate('{{12.34 1}}')
|
||||
.withInput({
|
||||
'12.34': function(arg) {
|
||||
return 'bar' + arg;
|
||||
}
|
||||
},
|
||||
'bar1'
|
||||
);
|
||||
})
|
||||
.toCompileTo('bar1');
|
||||
});
|
||||
|
||||
it('pass boolean literals', function() {
|
||||
shouldCompileTo('{{true}}', {}, '');
|
||||
shouldCompileTo('{{true}}', { '': 'foo' }, '');
|
||||
shouldCompileTo('{{false}}', { false: 'foo' }, 'foo');
|
||||
expectTemplate('{{true}}').toCompileTo('');
|
||||
|
||||
expectTemplate('{{true}}')
|
||||
.withInput({ '': 'foo' })
|
||||
.toCompileTo('');
|
||||
|
||||
expectTemplate('{{false}}')
|
||||
.withInput({ false: 'foo' })
|
||||
.toCompileTo('foo');
|
||||
});
|
||||
|
||||
it('should handle literals in subexpression', function() {
|
||||
var helpers = {
|
||||
foo: function(arg) {
|
||||
expectTemplate('{{foo (false)}}')
|
||||
.withInput({
|
||||
false: function() {
|
||||
return 'bar';
|
||||
}
|
||||
})
|
||||
.withHelper('foo', function(arg) {
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{foo (false)}}',
|
||||
[
|
||||
{
|
||||
false: function() {
|
||||
return 'bar';
|
||||
}
|
||||
},
|
||||
helpers
|
||||
],
|
||||
'bar'
|
||||
);
|
||||
})
|
||||
.toCompileTo('bar');
|
||||
});
|
||||
});
|
||||
|
||||
+286
-335
@@ -1,455 +1,405 @@
|
||||
describe('blocks', function() {
|
||||
it('array', function() {
|
||||
var string = '{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!';
|
||||
var hash = {
|
||||
goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
|
||||
world: 'world'
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'goodbye! Goodbye! GOODBYE! cruel world!',
|
||||
'Arrays iterate over the contents when not empty'
|
||||
);
|
||||
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ goodbyes: [], world: 'world' },
|
||||
'cruel world!',
|
||||
'Arrays ignore the contents when empty'
|
||||
);
|
||||
expectTemplate(string)
|
||||
.withInput({
|
||||
goodbyes: [
|
||||
{ text: 'goodbye' },
|
||||
{ text: 'Goodbye' },
|
||||
{ text: 'GOODBYE' }
|
||||
],
|
||||
world: 'world'
|
||||
})
|
||||
.withMessage('Arrays iterate over the contents when not empty')
|
||||
.toCompileTo('goodbye! Goodbye! GOODBYE! cruel world!');
|
||||
|
||||
expectTemplate(string)
|
||||
.withInput({
|
||||
goodbyes: [],
|
||||
world: 'world'
|
||||
})
|
||||
.withMessage('Arrays ignore the contents when empty')
|
||||
.toCompileTo('cruel world!');
|
||||
});
|
||||
|
||||
it('array without data', function() {
|
||||
var string =
|
||||
'{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}';
|
||||
var hash = {
|
||||
goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
|
||||
world: 'world'
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, , , false],
|
||||
'goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#goodbyes}}{{text}}{{/goodbyes}} {{#goodbyes}}{{text}}{{/goodbyes}}'
|
||||
)
|
||||
.withInput({
|
||||
goodbyes: [
|
||||
{ text: 'goodbye' },
|
||||
{ text: 'Goodbye' },
|
||||
{ text: 'GOODBYE' }
|
||||
],
|
||||
world: 'world'
|
||||
})
|
||||
.withCompileOptions({ compat: false })
|
||||
.toCompileTo('goodbyeGoodbyeGOODBYE goodbyeGoodbyeGOODBYE');
|
||||
});
|
||||
|
||||
it('array with @index', function() {
|
||||
var string =
|
||||
'{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!';
|
||||
var hash = {
|
||||
goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
|
||||
world: 'world'
|
||||
};
|
||||
|
||||
var template = CompilerContext.compile(string);
|
||||
var result = template(hash);
|
||||
|
||||
equal(
|
||||
result,
|
||||
'0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!',
|
||||
'The @index variable is used'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!'
|
||||
)
|
||||
.withInput({
|
||||
goodbyes: [
|
||||
{ text: 'goodbye' },
|
||||
{ text: 'Goodbye' },
|
||||
{ text: 'GOODBYE' }
|
||||
],
|
||||
world: 'world'
|
||||
})
|
||||
.withMessage('The @index variable is used')
|
||||
.toCompileTo('0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!');
|
||||
});
|
||||
|
||||
it('empty block', function() {
|
||||
var string = '{{#goodbyes}}{{/goodbyes}}cruel {{world}}!';
|
||||
var hash = {
|
||||
goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }],
|
||||
world: 'world'
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'cruel world!',
|
||||
'Arrays iterate over the contents when not empty'
|
||||
);
|
||||
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ goodbyes: [], world: 'world' },
|
||||
'cruel world!',
|
||||
'Arrays ignore the contents when empty'
|
||||
);
|
||||
expectTemplate(string)
|
||||
.withInput({
|
||||
goodbyes: [
|
||||
{ text: 'goodbye' },
|
||||
{ text: 'Goodbye' },
|
||||
{ text: 'GOODBYE' }
|
||||
],
|
||||
world: 'world'
|
||||
})
|
||||
.withMessage('Arrays iterate over the contents when not empty')
|
||||
.toCompileTo('cruel world!');
|
||||
|
||||
expectTemplate(string)
|
||||
.withInput({
|
||||
goodbyes: [],
|
||||
world: 'world'
|
||||
})
|
||||
.withMessage('Arrays ignore the contents when empty')
|
||||
.toCompileTo('cruel world!');
|
||||
});
|
||||
|
||||
it('block with complex lookup', function() {
|
||||
var string = '{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}';
|
||||
var hash = {
|
||||
name: 'Alan',
|
||||
goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }]
|
||||
};
|
||||
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ',
|
||||
'Templates can access variables in contexts up the stack with relative path syntax'
|
||||
);
|
||||
expectTemplate('{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}')
|
||||
.withInput({
|
||||
name: 'Alan',
|
||||
goodbyes: [
|
||||
{ text: 'goodbye' },
|
||||
{ text: 'Goodbye' },
|
||||
{ text: 'GOODBYE' }
|
||||
]
|
||||
})
|
||||
.withMessage(
|
||||
'Templates can access variables in contexts up the stack with relative path syntax'
|
||||
)
|
||||
.toCompileTo(
|
||||
'goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! '
|
||||
);
|
||||
});
|
||||
|
||||
it('multiple blocks with complex lookup', function() {
|
||||
var string = '{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}';
|
||||
var hash = {
|
||||
name: 'Alan',
|
||||
goodbyes: [{ text: 'goodbye' }, { text: 'Goodbye' }, { text: 'GOODBYE' }]
|
||||
};
|
||||
|
||||
shouldCompileTo(string, hash, 'AlanAlanAlanAlanAlanAlan');
|
||||
expectTemplate('{{#goodbyes}}{{../name}}{{../name}}{{/goodbyes}}')
|
||||
.withInput({
|
||||
name: 'Alan',
|
||||
goodbyes: [
|
||||
{ text: 'goodbye' },
|
||||
{ text: 'Goodbye' },
|
||||
{ text: 'GOODBYE' }
|
||||
]
|
||||
})
|
||||
.toCompileTo('AlanAlanAlanAlanAlanAlan');
|
||||
});
|
||||
|
||||
it('block with complex lookup using nested context', function() {
|
||||
var string = '{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}';
|
||||
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(string);
|
||||
}, Error);
|
||||
expectTemplate(
|
||||
'{{#goodbyes}}{{text}} cruel {{foo/../name}}! {{/goodbyes}}'
|
||||
).toThrow(Error);
|
||||
});
|
||||
|
||||
it('block with deep nested complex lookup', function() {
|
||||
var string =
|
||||
'{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}';
|
||||
var hash = {
|
||||
omg: 'OMG!',
|
||||
outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }]
|
||||
};
|
||||
|
||||
shouldCompileTo(string, hash, 'Goodbye cruel sad OMG!');
|
||||
expectTemplate(
|
||||
'{{#outer}}Goodbye {{#inner}}cruel {{../sibling}} {{../../omg}}{{/inner}}{{/outer}}'
|
||||
)
|
||||
.withInput({
|
||||
omg: 'OMG!',
|
||||
outer: [{ sibling: 'sad', inner: [{ text: 'goodbye' }] }]
|
||||
})
|
||||
.toCompileTo('Goodbye cruel sad OMG!');
|
||||
});
|
||||
|
||||
it('works with cached blocks', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}',
|
||||
{ data: false }
|
||||
);
|
||||
|
||||
var result = template({
|
||||
person: [
|
||||
{ first: 'Alan', last: 'Johnson' },
|
||||
{ first: 'Alan', last: 'Johnson' }
|
||||
]
|
||||
});
|
||||
equals(result, 'Alan JohnsonAlan Johnson');
|
||||
expectTemplate(
|
||||
'{{#each person}}{{#with .}}{{first}} {{last}}{{/with}}{{/each}}'
|
||||
)
|
||||
.withCompileOptions({ data: false })
|
||||
.withInput({
|
||||
person: [
|
||||
{ first: 'Alan', last: 'Johnson' },
|
||||
{ first: 'Alan', last: 'Johnson' }
|
||||
]
|
||||
})
|
||||
.toCompileTo('Alan JohnsonAlan Johnson');
|
||||
});
|
||||
|
||||
describe('inverted sections', function() {
|
||||
it('inverted sections with unset value', function() {
|
||||
var string =
|
||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}';
|
||||
var hash = {};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'Right On!',
|
||||
"Inverted section rendered when value isn't set."
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
|
||||
)
|
||||
.withMessage("Inverted section rendered when value isn't set.")
|
||||
.toCompileTo('Right On!');
|
||||
});
|
||||
|
||||
it('inverted section with false value', function() {
|
||||
var string =
|
||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}';
|
||||
var hash = { goodbyes: false };
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'Right On!',
|
||||
'Inverted section rendered when value is false.'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
|
||||
)
|
||||
.withInput({ goodbyes: false })
|
||||
.withMessage('Inverted section rendered when value is false.')
|
||||
.toCompileTo('Right On!');
|
||||
});
|
||||
|
||||
it('inverted section with empty set', function() {
|
||||
var string =
|
||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}';
|
||||
var hash = { goodbyes: [] };
|
||||
shouldCompileTo(
|
||||
string,
|
||||
hash,
|
||||
'Right On!',
|
||||
'Inverted section rendered when value is empty set.'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}'
|
||||
)
|
||||
.withInput({ goodbyes: [] })
|
||||
.withMessage('Inverted section rendered when value is empty set.')
|
||||
.toCompileTo('Right On!');
|
||||
});
|
||||
|
||||
it('block inverted sections', function() {
|
||||
shouldCompileTo(
|
||||
'{{#people}}{{name}}{{^}}{{none}}{{/people}}',
|
||||
{ none: 'No people' },
|
||||
'No people'
|
||||
);
|
||||
expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}')
|
||||
.withInput({ none: 'No people' })
|
||||
.toCompileTo('No people');
|
||||
});
|
||||
|
||||
it('chained inverted sections', function() {
|
||||
shouldCompileTo(
|
||||
'{{#people}}{{name}}{{else if none}}{{none}}{{/people}}',
|
||||
{ none: 'No people' },
|
||||
'No people'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}',
|
||||
{ none: 'No people' },
|
||||
'No people'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}',
|
||||
{ none: 'No people' },
|
||||
'No people'
|
||||
);
|
||||
expectTemplate('{{#people}}{{name}}{{else if none}}{{none}}{{/people}}')
|
||||
.withInput({ none: 'No people' })
|
||||
.toCompileTo('No people');
|
||||
|
||||
expectTemplate(
|
||||
'{{#people}}{{name}}{{else if nothere}}fail{{else unless nothere}}{{none}}{{/people}}'
|
||||
)
|
||||
.withInput({ none: 'No people' })
|
||||
.toCompileTo('No people');
|
||||
|
||||
expectTemplate(
|
||||
'{{#people}}{{name}}{{else if none}}{{none}}{{else}}fail{{/people}}'
|
||||
)
|
||||
.withInput({ none: 'No people' })
|
||||
.toCompileTo('No people');
|
||||
});
|
||||
|
||||
it('chained inverted sections with mismatch', function() {
|
||||
shouldThrow(function() {
|
||||
shouldCompileTo(
|
||||
'{{#people}}{{name}}{{else if none}}{{none}}{{/if}}',
|
||||
{ none: 'No people' },
|
||||
'No people'
|
||||
);
|
||||
}, Error);
|
||||
expectTemplate(
|
||||
'{{#people}}{{name}}{{else if none}}{{none}}{{/if}}'
|
||||
).toThrow(Error);
|
||||
});
|
||||
|
||||
it('block inverted sections with empty arrays', function() {
|
||||
shouldCompileTo(
|
||||
'{{#people}}{{name}}{{^}}{{none}}{{/people}}',
|
||||
{ none: 'No people', people: [] },
|
||||
'No people'
|
||||
);
|
||||
expectTemplate('{{#people}}{{name}}{{^}}{{none}}{{/people}}')
|
||||
.withInput({
|
||||
none: 'No people',
|
||||
people: []
|
||||
})
|
||||
.toCompileTo('No people');
|
||||
});
|
||||
});
|
||||
|
||||
describe('standalone sections', function() {
|
||||
it('block standalone else sections', function() {
|
||||
shouldCompileTo(
|
||||
'{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n',
|
||||
{ none: 'No people' },
|
||||
'No people\n'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{#none}}\n{{.}}\n{{^}}\n{{none}}\n{{/none}}\n',
|
||||
{ none: 'No people' },
|
||||
'No people\n'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n',
|
||||
{ none: 'No people' },
|
||||
'No people\n'
|
||||
);
|
||||
expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
|
||||
.withInput({ none: 'No people' })
|
||||
.toCompileTo('No people\n');
|
||||
|
||||
expectTemplate('{{#none}}\n{{.}}\n{{^}}\n{{none}}\n{{/none}}\n')
|
||||
.withInput({ none: 'No people' })
|
||||
.toCompileTo('No people\n');
|
||||
|
||||
expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
|
||||
.withInput({ none: 'No people' })
|
||||
.toCompileTo('No people\n');
|
||||
});
|
||||
|
||||
it('block standalone else sections can be disabled', function() {
|
||||
shouldCompileTo(
|
||||
'{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n',
|
||||
[{ none: 'No people' }, {}, {}, { ignoreStandalone: true }],
|
||||
'\nNo people\n\n'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{#none}}\n{{.}}\n{{^}}\nFail\n{{/none}}\n',
|
||||
[{ none: 'No people' }, {}, {}, { ignoreStandalone: true }],
|
||||
'\nNo people\n\n'
|
||||
);
|
||||
expectTemplate('{{#people}}\n{{name}}\n{{^}}\n{{none}}\n{{/people}}\n')
|
||||
.withInput({ none: 'No people' })
|
||||
.withCompileOptions({ ignoreStandalone: true })
|
||||
.toCompileTo('\nNo people\n\n');
|
||||
|
||||
expectTemplate('{{#none}}\n{{.}}\n{{^}}\nFail\n{{/none}}\n')
|
||||
.withInput({ none: 'No people' })
|
||||
.withCompileOptions({ ignoreStandalone: true })
|
||||
.toCompileTo('\nNo people\n\n');
|
||||
});
|
||||
|
||||
it('block standalone chained else sections', function() {
|
||||
shouldCompileTo(
|
||||
'{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n',
|
||||
{ none: 'No people' },
|
||||
'No people\n'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n',
|
||||
{ none: 'No people' },
|
||||
'No people\n'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{/people}}\n'
|
||||
)
|
||||
.withInput({ none: 'No people' })
|
||||
.toCompileTo('No people\n');
|
||||
|
||||
expectTemplate(
|
||||
'{{#people}}\n{{name}}\n{{else if none}}\n{{none}}\n{{^}}\n{{/people}}\n'
|
||||
)
|
||||
.withInput({ none: 'No people' })
|
||||
.toCompileTo('No people\n');
|
||||
});
|
||||
|
||||
it('should handle nesting', function() {
|
||||
shouldCompileTo(
|
||||
'{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.',
|
||||
{ data: [1, 3, 5] },
|
||||
'1\n3\n5\nOK.'
|
||||
);
|
||||
expectTemplate('{{#data}}\n{{#if true}}\n{{.}}\n{{/if}}\n{{/data}}\nOK.')
|
||||
.withInput({
|
||||
data: [1, 3, 5]
|
||||
})
|
||||
.toCompileTo('1\n3\n5\nOK.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('compat mode', function() {
|
||||
it('block with deep recursive lookup lookup', function() {
|
||||
var string =
|
||||
'{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}';
|
||||
var hash = { omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] };
|
||||
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, undefined, undefined, true],
|
||||
'Goodbye cruel OMG!'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#outer}}Goodbye {{#inner}}cruel {{omg}}{{/inner}}{{/outer}}'
|
||||
)
|
||||
.withInput({ omg: 'OMG!', outer: [{ inner: [{ text: 'goodbye' }] }] })
|
||||
.withCompileOptions({ compat: true })
|
||||
.toCompileTo('Goodbye cruel OMG!');
|
||||
});
|
||||
|
||||
it('block with deep recursive pathed lookup', function() {
|
||||
var string =
|
||||
'{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}';
|
||||
var hash = {
|
||||
omg: { yes: 'OMG!' },
|
||||
outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }]
|
||||
};
|
||||
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, undefined, undefined, true],
|
||||
'Goodbye cruel OMG!'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'
|
||||
)
|
||||
.withInput({
|
||||
omg: { yes: 'OMG!' },
|
||||
outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }]
|
||||
})
|
||||
.withCompileOptions({ compat: true })
|
||||
.toCompileTo('Goodbye cruel OMG!');
|
||||
});
|
||||
it('block with missed recursive lookup', function() {
|
||||
var string =
|
||||
'{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}';
|
||||
var hash = {
|
||||
omg: { no: 'OMG!' },
|
||||
outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }]
|
||||
};
|
||||
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[hash, undefined, undefined, true],
|
||||
'Goodbye cruel '
|
||||
);
|
||||
it('block with missed recursive lookup', function() {
|
||||
expectTemplate(
|
||||
'{{#outer}}Goodbye {{#inner}}cruel {{omg.yes}}{{/inner}}{{/outer}}'
|
||||
)
|
||||
.withInput({
|
||||
omg: { no: 'OMG!' },
|
||||
outer: [{ inner: [{ yes: 'no', text: 'goodbye' }] }]
|
||||
})
|
||||
.withCompileOptions({ compat: true })
|
||||
.toCompileTo('Goodbye cruel ');
|
||||
});
|
||||
});
|
||||
|
||||
describe('decorators', function() {
|
||||
it('should apply mustache decorators', function() {
|
||||
var helpers = {
|
||||
helper: function(options) {
|
||||
expectTemplate('{{#helper}}{{*decorator}}{{/helper}}')
|
||||
.withHelper('helper', function(options) {
|
||||
return options.fn.run;
|
||||
}
|
||||
};
|
||||
var decorators = {
|
||||
decorator: function(fn) {
|
||||
})
|
||||
.withDecorator('decorator', function(fn) {
|
||||
fn.run = 'success';
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{*decorator}}{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
})
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should apply allow undefined return', function() {
|
||||
var helpers = {
|
||||
helper: function(options) {
|
||||
expectTemplate('{{#helper}}{{*decorator}}suc{{/helper}}')
|
||||
.withHelper('helper', function(options) {
|
||||
return options.fn() + options.fn.run;
|
||||
}
|
||||
};
|
||||
var decorators = {
|
||||
decorator: function(fn) {
|
||||
})
|
||||
.withDecorator('decorator', function(fn) {
|
||||
fn.run = 'cess';
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{*decorator}}suc{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
})
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should apply block decorators', function() {
|
||||
var helpers = {
|
||||
helper: function(options) {
|
||||
expectTemplate(
|
||||
'{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}'
|
||||
)
|
||||
.withHelper('helper', function(options) {
|
||||
return options.fn.run;
|
||||
}
|
||||
};
|
||||
var decorators = {
|
||||
decorator: function(fn, props, container, options) {
|
||||
})
|
||||
.withDecorator('decorator', function(fn, props, container, options) {
|
||||
fn.run = options.fn();
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{#*decorator}}success{{/decorator}}{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
})
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should support nested decorators', function() {
|
||||
var helpers = {
|
||||
helper: function(options) {
|
||||
expectTemplate(
|
||||
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}'
|
||||
)
|
||||
.withHelper('helper', function(options) {
|
||||
return options.fn.run;
|
||||
}
|
||||
};
|
||||
var decorators = {
|
||||
decorator: function(fn, props, container, options) {
|
||||
fn.run = options.fn.nested + options.fn();
|
||||
return fn;
|
||||
},
|
||||
nested: function(fn, props, container, options) {
|
||||
props.nested = options.fn();
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{#*decorator}}{{#*nested}}suc{{/nested}}cess{{/decorator}}{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
})
|
||||
.withDecorators({
|
||||
decorator: function(fn, props, container, options) {
|
||||
fn.run = options.fn.nested + options.fn();
|
||||
return fn;
|
||||
},
|
||||
nested: function(fn, props, container, options) {
|
||||
props.nested = options.fn();
|
||||
}
|
||||
})
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should apply multiple decorators', function() {
|
||||
var helpers = {
|
||||
helper: function(options) {
|
||||
expectTemplate(
|
||||
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}'
|
||||
)
|
||||
.withHelper('helper', function(options) {
|
||||
return options.fn.run;
|
||||
}
|
||||
};
|
||||
var decorators = {
|
||||
decorator: function(fn, props, container, options) {
|
||||
})
|
||||
.withDecorator('decorator', function(fn, props, container, options) {
|
||||
fn.run = (fn.run || '') + options.fn();
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{#*decorator}}suc{{/decorator}}{{#*decorator}}cess{{/decorator}}{{/helper}}',
|
||||
{ hash: {}, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
})
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should access parent variables', function() {
|
||||
var helpers = {
|
||||
helper: function(options) {
|
||||
expectTemplate('{{#helper}}{{*decorator foo}}{{/helper}}')
|
||||
.withHelper('helper', function(options) {
|
||||
return options.fn.run;
|
||||
}
|
||||
};
|
||||
var decorators = {
|
||||
decorator: function(fn, props, container, options) {
|
||||
})
|
||||
.withDecorator('decorator', function(fn, props, container, options) {
|
||||
fn.run = options.args;
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{#helper}}{{*decorator foo}}{{/helper}}',
|
||||
{ hash: { foo: 'success' }, helpers: helpers, decorators: decorators },
|
||||
'success'
|
||||
);
|
||||
})
|
||||
.withInput({ foo: 'success' })
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should work with root program', function() {
|
||||
var run;
|
||||
var decorators = {
|
||||
decorator: function(fn, props, container, options) {
|
||||
expectTemplate('{{*decorator "success"}}')
|
||||
.withDecorator('decorator', function(fn, props, container, options) {
|
||||
equals(options.args[0], 'success');
|
||||
run = true;
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{*decorator "success"}}',
|
||||
{ hash: { foo: 'success' }, decorators: decorators },
|
||||
''
|
||||
);
|
||||
})
|
||||
.withInput({ foo: 'success' })
|
||||
.toCompileTo('');
|
||||
equals(run, true);
|
||||
});
|
||||
|
||||
it('should fail when accessing variables from root', function() {
|
||||
var run;
|
||||
var decorators = {
|
||||
decorator: function(fn, props, container, options) {
|
||||
expectTemplate('{{*decorator foo}}')
|
||||
.withDecorator('decorator', function(fn, props, container, options) {
|
||||
equals(options.args[0], undefined);
|
||||
run = true;
|
||||
return fn;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
'{{*decorator foo}}',
|
||||
{ hash: { foo: 'fail' }, decorators: decorators },
|
||||
''
|
||||
);
|
||||
})
|
||||
.withInput({ foo: 'fail' })
|
||||
.toCompileTo('');
|
||||
equals(run, true);
|
||||
});
|
||||
|
||||
@@ -481,6 +431,7 @@ describe('blocks', function() {
|
||||
equals(handlebarsEnv.decorators.foo, undefined);
|
||||
equals(handlebarsEnv.decorators.bar, undefined);
|
||||
});
|
||||
|
||||
it('fails with multiple and args', function() {
|
||||
shouldThrow(
|
||||
function() {
|
||||
|
||||
+462
-449
File diff suppressed because it is too large
Load Diff
+157
-243
@@ -1,31 +1,24 @@
|
||||
describe('data', function() {
|
||||
it('passing in data to a compiled function that expects data - works with helpers', function() {
|
||||
var template = CompilerContext.compile('{{hello}}', { data: true });
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
expectTemplate('{{hello}}')
|
||||
.withCompileOptions({ data: true })
|
||||
.withHelper('hello', function(options) {
|
||||
return options.data.adjective + ' ' + this.noun;
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{ noun: 'cat' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy cat', result, 'Data output by helper');
|
||||
})
|
||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||
.withInput({ noun: 'cat' })
|
||||
.withMessage('Data output by helper')
|
||||
.toCompileTo('happy cat');
|
||||
});
|
||||
|
||||
it('data can be looked up via @foo', function() {
|
||||
var template = CompilerContext.compile('{{@hello}}');
|
||||
var result = template({}, { data: { hello: 'hello' } });
|
||||
equals('hello', result, '@foo retrieves template data');
|
||||
expectTemplate('{{@hello}}')
|
||||
.withRuntimeOptions({ data: { hello: 'hello' } })
|
||||
.withMessage('@foo retrieves template data')
|
||||
.toCompileTo('hello');
|
||||
});
|
||||
|
||||
it('deep @foo triggers automatic top-level data', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}'
|
||||
);
|
||||
|
||||
var helpers = Handlebars.createFrame(handlebarsEnv.helpers);
|
||||
|
||||
helpers.let = function(options) {
|
||||
@@ -39,124 +32,92 @@ describe('data', function() {
|
||||
return options.fn(this, { data: frame });
|
||||
};
|
||||
|
||||
var result = template({ foo: true }, { helpers: helpers });
|
||||
equals('Hello world', result, 'Automatic data was triggered');
|
||||
expectTemplate(
|
||||
'{{#let world="world"}}{{#if foo}}{{#if foo}}Hello {{@world}}{{/if}}{{/if}}{{/let}}'
|
||||
)
|
||||
.withInput({ foo: true })
|
||||
.withHelpers(helpers)
|
||||
.withMessage('Automatic data was triggered')
|
||||
.toCompileTo('Hello world');
|
||||
});
|
||||
|
||||
it('parameter data can be looked up via @foo', function() {
|
||||
var template = CompilerContext.compile('{{hello @world}}');
|
||||
var helpers = {
|
||||
hello: function(noun) {
|
||||
expectTemplate('{{hello @world}}')
|
||||
.withRuntimeOptions({ data: { world: 'world' } })
|
||||
.withHelper('hello', function(noun) {
|
||||
return 'Hello ' + noun;
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers, data: { world: 'world' } });
|
||||
equals(
|
||||
'Hello world',
|
||||
result,
|
||||
'@foo as a parameter retrieves template data'
|
||||
);
|
||||
})
|
||||
.withMessage('@foo as a parameter retrieves template data')
|
||||
.toCompileTo('Hello world');
|
||||
});
|
||||
|
||||
it('hash values can be looked up via @foo', function() {
|
||||
var template = CompilerContext.compile('{{hello noun=@world}}');
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
expectTemplate('{{hello noun=@world}}')
|
||||
.withRuntimeOptions({ data: { world: 'world' } })
|
||||
.withHelper('hello', function(options) {
|
||||
return 'Hello ' + options.hash.noun;
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers, data: { world: 'world' } });
|
||||
equals(
|
||||
'Hello world',
|
||||
result,
|
||||
'@foo as a parameter retrieves template data'
|
||||
);
|
||||
})
|
||||
.withMessage('@foo as a parameter retrieves template data')
|
||||
.toCompileTo('Hello world');
|
||||
});
|
||||
|
||||
it('nested parameter data can be looked up via @foo.bar', function() {
|
||||
var template = CompilerContext.compile('{{hello @world.bar}}');
|
||||
var helpers = {
|
||||
hello: function(noun) {
|
||||
expectTemplate('{{hello @world.bar}}')
|
||||
.withRuntimeOptions({ data: { world: { bar: 'world' } } })
|
||||
.withHelper('hello', function(noun) {
|
||||
return 'Hello ' + noun;
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{},
|
||||
{ helpers: helpers, data: { world: { bar: 'world' } } }
|
||||
);
|
||||
equals(
|
||||
'Hello world',
|
||||
result,
|
||||
'@foo as a parameter retrieves template data'
|
||||
);
|
||||
})
|
||||
.withMessage('@foo as a parameter retrieves template data')
|
||||
.toCompileTo('Hello world');
|
||||
});
|
||||
|
||||
it('nested parameter data does not fail with @world.bar', function() {
|
||||
var template = CompilerContext.compile('{{hello @world.bar}}');
|
||||
var helpers = {
|
||||
hello: function(noun) {
|
||||
expectTemplate('{{hello @world.bar}}')
|
||||
.withRuntimeOptions({ data: { foo: { bar: 'world' } } })
|
||||
.withHelper('hello', function(noun) {
|
||||
return 'Hello ' + noun;
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{},
|
||||
{ helpers: helpers, data: { foo: { bar: 'world' } } }
|
||||
);
|
||||
equals(
|
||||
'Hello undefined',
|
||||
result,
|
||||
'@foo as a parameter retrieves template data'
|
||||
);
|
||||
})
|
||||
.withMessage('@foo as a parameter retrieves template data')
|
||||
.toCompileTo('Hello undefined');
|
||||
});
|
||||
|
||||
it('parameter data throws when using complex scope references', function() {
|
||||
var string = '{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}';
|
||||
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile(string);
|
||||
}, Error);
|
||||
expectTemplate(
|
||||
'{{#goodbyes}}{{text}} cruel {{@foo/../name}}! {{/goodbyes}}'
|
||||
).toThrow(Error);
|
||||
});
|
||||
|
||||
it('data can be functions', function() {
|
||||
var template = CompilerContext.compile('{{@hello}}');
|
||||
var result = template(
|
||||
{},
|
||||
{
|
||||
expectTemplate('{{@hello}}')
|
||||
.withRuntimeOptions({
|
||||
data: {
|
||||
hello: function() {
|
||||
return 'hello';
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
equals('hello', result);
|
||||
})
|
||||
.toCompileTo('hello');
|
||||
});
|
||||
|
||||
it('data can be functions with params', function() {
|
||||
var template = CompilerContext.compile('{{@hello "hello"}}');
|
||||
var result = template(
|
||||
{},
|
||||
{
|
||||
expectTemplate('{{@hello "hello"}}')
|
||||
.withRuntimeOptions({
|
||||
data: {
|
||||
hello: function(arg) {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
equals('hello', result);
|
||||
})
|
||||
.toCompileTo('hello');
|
||||
});
|
||||
|
||||
it('data is inherited downstream', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}',
|
||||
{ data: true }
|
||||
);
|
||||
var helpers = {
|
||||
let: function(options) {
|
||||
expectTemplate(
|
||||
'{{#let foo=1 bar=2}}{{#let foo=bar.baz}}{{@bar}}{{@foo}}{{/let}}{{@foo}}{{/let}}'
|
||||
)
|
||||
.withInput({ bar: { baz: 'hello world' } })
|
||||
.withCompileOptions({ data: true })
|
||||
.withHelper('let', function(options) {
|
||||
var frame = Handlebars.createFrame(options.data);
|
||||
for (var prop in options.hash) {
|
||||
if (prop in options.hash) {
|
||||
@@ -164,201 +125,154 @@ describe('data', function() {
|
||||
}
|
||||
}
|
||||
return options.fn(this, { data: frame });
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{ bar: { baz: 'hello world' } },
|
||||
{ helpers: helpers, data: {} }
|
||||
);
|
||||
equals('2hello world1', result, 'data variables are inherited downstream');
|
||||
})
|
||||
.withRuntimeOptions({ data: {} })
|
||||
.withMessage('data variables are inherited downstream')
|
||||
.toCompileTo('2hello world1');
|
||||
});
|
||||
|
||||
it('passing in data to a compiled function that expects data - works with helpers in partials', function() {
|
||||
var template = CompilerContext.compile('{{>myPartial}}', { data: true });
|
||||
|
||||
var partials = {
|
||||
myPartial: CompilerContext.compile('{{hello}}', { data: true })
|
||||
};
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
expectTemplate('{{>myPartial}}')
|
||||
.withCompileOptions({ data: true })
|
||||
.withPartial('myPartial', '{{hello}}')
|
||||
.withHelper('hello', function(options) {
|
||||
return options.data.adjective + ' ' + this.noun;
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{ noun: 'cat' },
|
||||
{ helpers: helpers, partials: partials, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy cat', result, 'Data output by helper inside partial');
|
||||
})
|
||||
.withInput({ noun: 'cat' })
|
||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||
.withMessage('Data output by helper inside partial')
|
||||
.toCompileTo('happy cat');
|
||||
});
|
||||
|
||||
it('passing in data to a compiled function that expects data - works with helpers and parameters', function() {
|
||||
var template = CompilerContext.compile('{{hello world}}', { data: true });
|
||||
|
||||
var helpers = {
|
||||
hello: function(noun, options) {
|
||||
expectTemplate('{{hello world}}')
|
||||
.withCompileOptions({ data: true })
|
||||
.withHelper('hello', function(noun, options) {
|
||||
return options.data.adjective + ' ' + noun + (this.exclaim ? '!' : '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{ exclaim: true, world: 'world' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy world!', result, 'Data output by helper');
|
||||
})
|
||||
.withInput({ exclaim: true, world: 'world' })
|
||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||
.withMessage('Data output by helper')
|
||||
.toCompileTo('happy world!');
|
||||
});
|
||||
|
||||
it('passing in data to a compiled function that expects data - works with block helpers', function() {
|
||||
var template = CompilerContext.compile('{{#hello}}{{world}}{{/hello}}', {
|
||||
data: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
expectTemplate('{{#hello}}{{world}}{{/hello}}')
|
||||
.withCompileOptions({
|
||||
data: true
|
||||
})
|
||||
.withHelper('hello', function(options) {
|
||||
return options.fn(this);
|
||||
},
|
||||
world: function(options) {
|
||||
})
|
||||
.withHelper('world', function(options) {
|
||||
return options.data.adjective + ' world' + (this.exclaim ? '!' : '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{ exclaim: true },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy world!', result, 'Data output by helper');
|
||||
})
|
||||
.withInput({ exclaim: true })
|
||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||
.withMessage('Data output by helper')
|
||||
.toCompileTo('happy world!');
|
||||
});
|
||||
|
||||
it('passing in data to a compiled function that expects data - works with block helpers that use ..', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#hello}}{{world ../zomg}}{{/hello}}',
|
||||
{ data: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
|
||||
.withCompileOptions({ data: true })
|
||||
.withHelper('hello', function(options) {
|
||||
return options.fn({ exclaim: '?' });
|
||||
},
|
||||
world: function(thing, options) {
|
||||
})
|
||||
.withHelper('world', function(thing, options) {
|
||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{ exclaim: true, zomg: 'world' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('happy world?', result, 'Data output by helper');
|
||||
})
|
||||
.withInput({ exclaim: true, zomg: 'world' })
|
||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||
.withMessage('Data output by helper')
|
||||
.toCompileTo('happy world?');
|
||||
});
|
||||
|
||||
it('passing in data to a compiled function that expects data - data is passed to with block helpers where children use ..', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#hello}}{{world ../zomg}}{{/hello}}',
|
||||
{ data: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
|
||||
.withCompileOptions({ data: true })
|
||||
.withHelper('hello', function(options) {
|
||||
return options.data.accessData + ' ' + options.fn({ exclaim: '?' });
|
||||
},
|
||||
world: function(thing, options) {
|
||||
})
|
||||
.withHelper('world', function(thing, options) {
|
||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{ exclaim: true, zomg: 'world' },
|
||||
{ helpers: helpers, data: { adjective: 'happy', accessData: '#win' } }
|
||||
);
|
||||
equals('#win happy world?', result, 'Data output by helper');
|
||||
})
|
||||
.withInput({ exclaim: true, zomg: 'world' })
|
||||
.withRuntimeOptions({ data: { adjective: 'happy', accessData: '#win' } })
|
||||
.withMessage('Data output by helper')
|
||||
.toCompileTo('#win happy world?');
|
||||
});
|
||||
|
||||
it('you can override inherited data when invoking a helper', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#hello}}{{world zomg}}{{/hello}}',
|
||||
{ data: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
expectTemplate('{{#hello}}{{world zomg}}{{/hello}}')
|
||||
.withCompileOptions({ data: true })
|
||||
.withHelper('hello', function(options) {
|
||||
return options.fn(
|
||||
{ exclaim: '?', zomg: 'world' },
|
||||
{ data: { adjective: 'sad' } }
|
||||
);
|
||||
},
|
||||
world: function(thing, options) {
|
||||
})
|
||||
.withHelper('world', function(thing, options) {
|
||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{ exclaim: true, zomg: 'planet' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('sad world?', result, 'Overriden data output by helper');
|
||||
})
|
||||
.withInput({ exclaim: true, zomg: 'planet' })
|
||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||
.withMessage('Overriden data output by helper')
|
||||
.toCompileTo('sad world?');
|
||||
});
|
||||
|
||||
it('you can override inherited data when invoking a helper with depth', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#hello}}{{world ../zomg}}{{/hello}}',
|
||||
{ data: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
hello: function(options) {
|
||||
expectTemplate('{{#hello}}{{world ../zomg}}{{/hello}}')
|
||||
.withCompileOptions({ data: true })
|
||||
.withHelper('hello', function(options) {
|
||||
return options.fn({ exclaim: '?' }, { data: { adjective: 'sad' } });
|
||||
},
|
||||
world: function(thing, options) {
|
||||
})
|
||||
.withHelper('world', function(thing, options) {
|
||||
return options.data.adjective + ' ' + thing + (this.exclaim || '');
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{ exclaim: true, zomg: 'world' },
|
||||
{ helpers: helpers, data: { adjective: 'happy' } }
|
||||
);
|
||||
equals('sad world?', result, 'Overriden data output by helper');
|
||||
})
|
||||
.withInput({ exclaim: true, zomg: 'world' })
|
||||
.withRuntimeOptions({ data: { adjective: 'happy' } })
|
||||
.withMessage('Overriden data output by helper')
|
||||
.toCompileTo('sad world?');
|
||||
});
|
||||
|
||||
describe('@root', function() {
|
||||
it('the root context can be looked up via @root', function() {
|
||||
var template = CompilerContext.compile('{{@root.foo}}');
|
||||
var result = template({ foo: 'hello' }, { data: {} });
|
||||
equals('hello', result);
|
||||
expectTemplate('{{@root.foo}}')
|
||||
.withInput({ foo: 'hello' })
|
||||
.withRuntimeOptions({ data: {} })
|
||||
.toCompileTo('hello');
|
||||
|
||||
result = template({ foo: 'hello' }, {});
|
||||
equals('hello', result);
|
||||
expectTemplate('{{@root.foo}}')
|
||||
.withInput({ foo: 'hello' })
|
||||
.toCompileTo('hello');
|
||||
});
|
||||
|
||||
it('passed root values take priority', function() {
|
||||
var template = CompilerContext.compile('{{@root.foo}}');
|
||||
var result = template({}, { data: { root: { foo: 'hello' } } });
|
||||
equals('hello', result);
|
||||
expectTemplate('{{@root.foo}}')
|
||||
.withInput({ foo: 'should not be used' })
|
||||
.withRuntimeOptions({ data: { root: { foo: 'hello' } } })
|
||||
.toCompileTo('hello');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nesting', function() {
|
||||
it('the root context can be looked up via @root', function() {
|
||||
var template = CompilerContext.compile(
|
||||
expectTemplate(
|
||||
'{{#helper}}{{#helper}}{{@./depth}} {{@../depth}} {{@../../depth}}{{/helper}}{{/helper}}'
|
||||
);
|
||||
var result = template(
|
||||
{ foo: 'hello' },
|
||||
{
|
||||
helpers: {
|
||||
helper: function(options) {
|
||||
var frame = Handlebars.createFrame(options.data);
|
||||
frame.depth = options.data.depth + 1;
|
||||
return options.fn(this, { data: frame });
|
||||
}
|
||||
},
|
||||
)
|
||||
.withInput({ foo: 'hello' })
|
||||
.withHelper('helper', function(options) {
|
||||
var frame = Handlebars.createFrame(options.data);
|
||||
frame.depth = options.data.depth + 1;
|
||||
return options.fn(this, { data: frame });
|
||||
})
|
||||
.withRuntimeOptions({
|
||||
data: {
|
||||
depth: 0
|
||||
}
|
||||
}
|
||||
);
|
||||
equals('2 1 0', result);
|
||||
})
|
||||
.toCompileTo('2 1 0');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
+40
-7
@@ -129,6 +129,7 @@ function HandlebarsTestBench(templateAsString) {
|
||||
this.templateAsString = templateAsString;
|
||||
this.helpers = {};
|
||||
this.partials = {};
|
||||
this.decorators = {};
|
||||
this.input = {};
|
||||
this.message =
|
||||
'Template' + templateAsString + ' does not evaluate to expected output';
|
||||
@@ -146,11 +147,43 @@ HandlebarsTestBench.prototype.withHelper = function(name, helperFunction) {
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withHelpers = function(helperFunctions) {
|
||||
var self = this;
|
||||
Object.keys(helperFunctions).forEach(function(name) {
|
||||
self.withHelper(name, helperFunctions[name]);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withPartial = function(name, partialAsString) {
|
||||
this.partials[name] = partialAsString;
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withPartials = function(partials) {
|
||||
var self = this;
|
||||
Object.keys(partials).forEach(function(name) {
|
||||
self.withPartial(name, partials[name]);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withDecorator = function(
|
||||
name,
|
||||
decoratorFunction
|
||||
) {
|
||||
this.decorators[name] = decoratorFunction;
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withDecorators = function(decorators) {
|
||||
var self = this;
|
||||
Object.keys(decorators).forEach(function(name) {
|
||||
self.withDecorator(name, decorators[name]);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.withCompileOptions = function(compileOptions) {
|
||||
this.compileOptions = compileOptions;
|
||||
return this;
|
||||
@@ -167,19 +200,18 @@ HandlebarsTestBench.prototype.withMessage = function(message) {
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype.toCompileTo = function(expectedOutputAsString) {
|
||||
expect(this._compileAndExecute()).to.equal(expectedOutputAsString);
|
||||
expect(this._compileAndExecute()).to.equal(
|
||||
expectedOutputAsString,
|
||||
this.message
|
||||
);
|
||||
};
|
||||
|
||||
// see chai "to.throw" (https://www.chaijs.com/api/bdd/#method_throw)
|
||||
HandlebarsTestBench.prototype.toThrow = function(
|
||||
errorLike,
|
||||
errMsgMatcher,
|
||||
msg
|
||||
) {
|
||||
HandlebarsTestBench.prototype.toThrow = function(errorLike, errMsgMatcher) {
|
||||
var self = this;
|
||||
expect(function() {
|
||||
self._compileAndExecute();
|
||||
}).to.throw(errorLike, errMsgMatcher, msg);
|
||||
}).to.throw(errorLike, errMsgMatcher, this.message);
|
||||
};
|
||||
|
||||
HandlebarsTestBench.prototype._compileAndExecute = function() {
|
||||
@@ -202,5 +234,6 @@ HandlebarsTestBench.prototype._combineRuntimeOptions = function() {
|
||||
});
|
||||
combinedRuntimeOptions.helpers = this.helpers;
|
||||
combinedRuntimeOptions.partials = this.partials;
|
||||
combinedRuntimeOptions.decorators = this.decorators;
|
||||
return combinedRuntimeOptions;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(['handlebars.runtime'], function(Handlebars) {
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return templates['bom'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "a";
|
||||
},"useData":true});
|
||||
});
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return templates['bom'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "a";
|
||||
},"useData":true});
|
||||
});
|
||||
@@ -1,3 +1,3 @@
|
||||
{"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "";
|
||||
},"useData":true}
|
||||
return "";
|
||||
},"useData":true}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
define(['some-path/handlebars.runtime'], function(Handlebars) {
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "";
|
||||
},"useData":true});
|
||||
});
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "";
|
||||
},"useData":true});
|
||||
});
|
||||
@@ -1,8 +1,6 @@
|
||||
Precompile handlebar templates.
|
||||
Usage: handlebars [template|directory]...
|
||||
|
||||
Options:
|
||||
--help Outputs this message [boolean]
|
||||
-f, --output Output File [string]
|
||||
--map Source Map File [string]
|
||||
-a, --amd Exports amd style (require.js) [boolean]
|
||||
@@ -22,4 +20,5 @@ Options:
|
||||
-d, --data Include data when compiling [boolean]
|
||||
-e, --extension Template extension. [string] [default: "handlebars"]
|
||||
-b, --bom Removes the BOM (Byte Order Mark) from the beginning of the templates. [boolean]
|
||||
-v, --version Show version number [boolean]
|
||||
-v, --version Prints the current compiler version [boolean]
|
||||
--help Outputs this message [boolean]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
define(['handlebars.runtime'], function(Handlebars) {
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = someNameSpace = someNameSpace || {};
|
||||
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "";
|
||||
},"useData":true});
|
||||
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "";
|
||||
},"useData":true});
|
||||
return templates;
|
||||
});
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = someNameSpace = someNameSpace || {};
|
||||
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "";
|
||||
},"useData":true});
|
||||
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "";
|
||||
},"useData":true});
|
||||
return templates;
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
define(['handlebars.runtime'], function(Handlebars) {
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return templates['non.default.extension'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "<div>This is a test</div>";
|
||||
},"useData":true});
|
||||
});
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return templates['non.default.extension'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "<div>This is a test</div>";
|
||||
},"useData":true});
|
||||
});
|
||||
@@ -1,24 +1,25 @@
|
||||
define(['handlebars.runtime'], function(Handlebars) {
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return templates['known.helpers'] = template({"1":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
return " <div>Some known helper</div>\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"anotherHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"anotherHelper","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":3,"column":4},"end":{"line":5,"column":22}}})) != null ? stack1 : "");
|
||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return ""+" <div>Some known helper</div>\n"
|
||||
+ ((stack1 = lookupProperty(helpers,"anotherHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"anotherHelper","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":3,"column":4},"end":{"line":5,"column":22}}})) != null ? stack1 : "");
|
||||
},"2":function(container,depth0,helpers,partials,data) {
|
||||
return " <div>Another known helper</div>\n";
|
||||
return " <div>Another known helper</div>\n";
|
||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
return ((stack1 = lookupProperty(helpers,"someHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"someHelper","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":15}}})) != null ? stack1 : "");
|
||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return ""+((stack1 = lookupProperty(helpers,"someHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"someHelper","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":15}}})) != null ? stack1 : "");
|
||||
},"useData":true});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
define(['handlebars.runtime'], function(Handlebars) {
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return Handlebars.partials['partial.template'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "<div>Test Partial</div>";
|
||||
},"useData":true});
|
||||
});
|
||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
||||
return Handlebars.partials['partial.template'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||
return "<div>Test Partial</div>";
|
||||
},"useData":true});
|
||||
});
|
||||
+527
-822
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -62,7 +62,7 @@
|
||||
}
|
||||
var runner = mocha.run();
|
||||
|
||||
//Reporting for saucelabs
|
||||
// Reporting to test-runner
|
||||
var failedTests = [];
|
||||
runner.on('end', function(){
|
||||
window.mochaResults = runner.stats;
|
||||
|
||||
@@ -20,16 +20,21 @@ describe('javascript-compiler api', function() {
|
||||
return parent + '.bar_' + name;
|
||||
};
|
||||
/* eslint-disable camelcase */
|
||||
shouldCompileTo('{{foo}}', { bar_foo: 'food' }, 'food');
|
||||
expectTemplate('{{foo}}')
|
||||
.withInput({ bar_foo: 'food' })
|
||||
.toCompileTo('food');
|
||||
/* eslint-enable camelcase */
|
||||
});
|
||||
|
||||
// Tests nameLookup dot vs. bracket behavior. Bracket is required in certain cases
|
||||
// to avoid errors in older browsers.
|
||||
it('should handle reserved words', function() {
|
||||
shouldCompileTo('{{foo}} {{~null~}}', { foo: 'food' }, 'food');
|
||||
expectTemplate('{{foo}} {{~null~}}')
|
||||
.withInput({ foo: 'food' })
|
||||
.toCompileTo('food');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#compilerInfo', function() {
|
||||
var $superCheck, $superInfo;
|
||||
beforeEach(function() {
|
||||
@@ -49,9 +54,12 @@ describe('javascript-compiler api', function() {
|
||||
throw new Error("It didn't work");
|
||||
}
|
||||
};
|
||||
shouldCompileTo('{{foo}} ', { foo: 'food' }, 'food ');
|
||||
expectTemplate('{{foo}} ')
|
||||
.withInput({ foo: 'food' })
|
||||
.toCompileTo('food ');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buffer', function() {
|
||||
var $superAppend, $superCreate;
|
||||
beforeEach(function() {
|
||||
@@ -70,7 +78,9 @@ describe('javascript-compiler api', function() {
|
||||
handlebarsEnv.JavaScriptCompiler.prototype.initializeBuffer = function() {
|
||||
return this.quotedString('foo_');
|
||||
};
|
||||
shouldCompileTo('{{foo}} ', { foo: 'food' }, 'foo_food ');
|
||||
expectTemplate('{{foo}} ')
|
||||
.withInput({ foo: 'food' })
|
||||
.toCompileTo('foo_food ');
|
||||
});
|
||||
it('should allow append buffer override', function() {
|
||||
handlebarsEnv.JavaScriptCompiler.prototype.appendToBuffer = function(
|
||||
@@ -78,7 +88,9 @@ describe('javascript-compiler api', function() {
|
||||
) {
|
||||
return $superAppend.call(this, [string, ' + "_foo"']);
|
||||
};
|
||||
shouldCompileTo('{{foo}}', { foo: 'food' }, 'food_foo');
|
||||
expectTemplate('{{foo}}')
|
||||
.withInput({ foo: 'food' })
|
||||
.toCompileTo('food_foo');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -106,4 +118,13 @@ describe('javascript-compiler api', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('options', function() {
|
||||
it('should append `noEscape` statements as string', function() {
|
||||
expectTemplate('{{a}}{{b}}')
|
||||
.withCompileOptions({ noEscape: true })
|
||||
.withInput({ a: 1, b: 1 })
|
||||
.toCompileTo('11');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+442
-606
File diff suppressed because it is too large
Load Diff
+217
-244
@@ -1,61 +1,53 @@
|
||||
describe('Regressions', function() {
|
||||
it('GH-94: Cannot read property of undefined', function() {
|
||||
var data = {
|
||||
books: [
|
||||
{
|
||||
title: 'The origin of species',
|
||||
author: {
|
||||
name: 'Charles Darwin'
|
||||
expectTemplate('{{#books}}{{title}}{{author.name}}{{/books}}')
|
||||
.withInput({
|
||||
books: [
|
||||
{
|
||||
title: 'The origin of species',
|
||||
author: {
|
||||
name: 'Charles Darwin'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Lazarillo de Tormes'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Lazarillo de Tormes'
|
||||
}
|
||||
]
|
||||
};
|
||||
var string = '{{#books}}{{title}}{{author.name}}{{/books}}';
|
||||
shouldCompileTo(
|
||||
string,
|
||||
data,
|
||||
'The origin of speciesCharles DarwinLazarillo de Tormes',
|
||||
'Renders without an undefined property error'
|
||||
);
|
||||
]
|
||||
})
|
||||
.withMessage('Renders without an undefined property error')
|
||||
.toCompileTo('The origin of speciesCharles DarwinLazarillo de Tormes');
|
||||
});
|
||||
|
||||
it("GH-150: Inverted sections print when they shouldn't", function() {
|
||||
var string = '{{^set}}not set{{/set}} :: {{#set}}set{{/set}}';
|
||||
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{},
|
||||
'not set :: ',
|
||||
"inverted sections run when property isn't present in context"
|
||||
);
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ set: undefined },
|
||||
'not set :: ',
|
||||
'inverted sections run when property is undefined'
|
||||
);
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ set: false },
|
||||
'not set :: ',
|
||||
'inverted sections run when property is false'
|
||||
);
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ set: true },
|
||||
' :: set',
|
||||
"inverted sections don't run when property is true"
|
||||
);
|
||||
expectTemplate(string)
|
||||
.withMessage(
|
||||
"inverted sections run when property isn't present in context"
|
||||
)
|
||||
.toCompileTo('not set :: ');
|
||||
|
||||
expectTemplate(string)
|
||||
.withInput({ set: undefined })
|
||||
.withMessage('inverted sections run when property is undefined')
|
||||
.toCompileTo('not set :: ');
|
||||
|
||||
expectTemplate(string)
|
||||
.withInput({ set: false })
|
||||
.withMessage('inverted sections run when property is false')
|
||||
.toCompileTo('not set :: ');
|
||||
|
||||
expectTemplate(string)
|
||||
.withInput({ set: true })
|
||||
.withMessage("inverted sections don't run when property is true")
|
||||
.toCompileTo(' :: set');
|
||||
});
|
||||
|
||||
it('GH-158: Using array index twice, breaks the template', function() {
|
||||
var string = '{{arr.[0]}}, {{arr.[1]}}';
|
||||
var data = { arr: [1, 2] };
|
||||
|
||||
shouldCompileTo(string, data, '1, 2', 'it works as expected');
|
||||
expectTemplate('{{arr.[0]}}, {{arr.[1]}}')
|
||||
.withInput({ arr: [1, 2] })
|
||||
.withMessage('it works as expected')
|
||||
.toCompileTo('1, 2');
|
||||
});
|
||||
|
||||
it("bug reported by @fat where lambdas weren't being properly resolved", function() {
|
||||
@@ -73,6 +65,7 @@ describe('Regressions', function() {
|
||||
'\n' +
|
||||
'<small>Nothing to check out...</small>\n' +
|
||||
'{{/hasThings}}';
|
||||
|
||||
var data = {
|
||||
thing: function() {
|
||||
return 'blah';
|
||||
@@ -95,25 +88,22 @@ describe('Regressions', function() {
|
||||
'<li class=two>@dhg</li>\n' +
|
||||
'<li class=three>@sayrer</li>\n' +
|
||||
'</ul>.\n';
|
||||
shouldCompileTo(string, data, output);
|
||||
|
||||
expectTemplate(string)
|
||||
.withInput(data)
|
||||
.toCompileTo(output);
|
||||
});
|
||||
|
||||
it('GH-408: Multiple loops fail', function() {
|
||||
var context = [
|
||||
{ name: 'John Doe', location: { city: 'Chicago' } },
|
||||
{ name: 'Jane Doe', location: { city: 'New York' } }
|
||||
];
|
||||
|
||||
var template = CompilerContext.compile(
|
||||
expectTemplate(
|
||||
'{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}{{#.}}{{name}}{{/.}}'
|
||||
);
|
||||
|
||||
var result = template(context);
|
||||
equals(
|
||||
result,
|
||||
'John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe',
|
||||
'It should output multiple times'
|
||||
);
|
||||
)
|
||||
.withInput([
|
||||
{ name: 'John Doe', location: { city: 'Chicago' } },
|
||||
{ name: 'Jane Doe', location: { city: 'New York' } }
|
||||
])
|
||||
.withMessage('It should output multiple times')
|
||||
.toCompileTo('John DoeJane DoeJohn DoeJane DoeJohn DoeJane Doe');
|
||||
});
|
||||
|
||||
it('GS-428: Nested if else rendering', function() {
|
||||
@@ -131,259 +121,242 @@ describe('Regressions', function() {
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo(succeedingTemplate, [{}, helpers], ' Expected ');
|
||||
shouldCompileTo(failingTemplate, [{}, helpers], ' Expected ');
|
||||
expectTemplate(succeedingTemplate)
|
||||
.withHelpers(helpers)
|
||||
.toCompileTo(' Expected ');
|
||||
|
||||
expectTemplate(failingTemplate)
|
||||
.withHelpers(helpers)
|
||||
.toCompileTo(' Expected ');
|
||||
});
|
||||
|
||||
it('GH-458: Scoped this identifier', function() {
|
||||
shouldCompileTo('{{./foo}}', { foo: 'bar' }, 'bar');
|
||||
expectTemplate('{{./foo}}')
|
||||
.withInput({ foo: 'bar' })
|
||||
.toCompileTo('bar');
|
||||
});
|
||||
|
||||
it('GH-375: Unicode line terminators', function() {
|
||||
shouldCompileTo('\u2028', {}, '\u2028');
|
||||
expectTemplate('\u2028').toCompileTo('\u2028');
|
||||
});
|
||||
|
||||
it('GH-534: Object prototype aliases', function() {
|
||||
/* eslint-disable no-extend-native */
|
||||
Object.prototype[0xd834] = true;
|
||||
|
||||
shouldCompileTo('{{foo}}', { foo: 'bar' }, 'bar');
|
||||
expectTemplate('{{foo}}')
|
||||
.withInput({ foo: 'bar' })
|
||||
.toCompileTo('bar');
|
||||
|
||||
delete Object.prototype[0xd834];
|
||||
/* eslint-enable no-extend-native */
|
||||
});
|
||||
|
||||
it('GH-437: Matching escaping', function() {
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile('{{{a}}');
|
||||
}, Error);
|
||||
shouldThrow(function() {
|
||||
CompilerContext.compile('{{a}}}');
|
||||
}, Error);
|
||||
expectTemplate('{{{a}}').toThrow(Error, /Parse error on/);
|
||||
expectTemplate('{{a}}}').toThrow(Error, /Parse error on/);
|
||||
});
|
||||
|
||||
it('GH-676: Using array in escaping mustache fails', function() {
|
||||
var string = '{{arr}}';
|
||||
var data = { arr: [1, 2] };
|
||||
|
||||
shouldCompileTo(string, data, data.arr.toString(), 'it works as expected');
|
||||
expectTemplate('{{arr}}')
|
||||
.withInput(data)
|
||||
.withMessage('it works as expected')
|
||||
.toCompileTo(data.arr.toString());
|
||||
});
|
||||
|
||||
it('Mustache man page', function() {
|
||||
var string =
|
||||
'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}';
|
||||
var data = {
|
||||
name: 'Chris',
|
||||
value: 10000,
|
||||
taxed_value: 10000 - 10000 * 0.4,
|
||||
in_ca: true
|
||||
};
|
||||
|
||||
shouldCompileTo(
|
||||
string,
|
||||
data,
|
||||
'Hello Chris. You have just won $10000! Well, $6000, after taxes.',
|
||||
'the hello world mustache example works'
|
||||
);
|
||||
expectTemplate(
|
||||
'Hello {{name}}. You have just won ${{value}}!{{#in_ca}} Well, ${{taxed_value}}, after taxes.{{/in_ca}}'
|
||||
)
|
||||
.withInput({
|
||||
name: 'Chris',
|
||||
value: 10000,
|
||||
taxed_value: 10000 - 10000 * 0.4,
|
||||
in_ca: true
|
||||
})
|
||||
.withMessage('the hello world mustache example works')
|
||||
.toCompileTo(
|
||||
'Hello Chris. You have just won $10000! Well, $6000, after taxes.'
|
||||
);
|
||||
});
|
||||
|
||||
it('GH-731: zero context rendering', function() {
|
||||
shouldCompileTo(
|
||||
'{{#foo}} This is {{bar}} ~ {{/foo}}',
|
||||
{ foo: 0, bar: 'OK' },
|
||||
' This is ~ '
|
||||
);
|
||||
expectTemplate('{{#foo}} This is {{bar}} ~ {{/foo}}')
|
||||
.withInput({
|
||||
foo: 0,
|
||||
bar: 'OK'
|
||||
})
|
||||
.toCompileTo(' This is ~ ');
|
||||
});
|
||||
|
||||
it('GH-820: zero pathed rendering', function() {
|
||||
shouldCompileTo('{{foo.bar}}', { foo: 0 }, '');
|
||||
expectTemplate('{{foo.bar}}')
|
||||
.withInput({ foo: 0 })
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('GH-837: undefined values for helpers', function() {
|
||||
var helpers = {
|
||||
str: function(value) {
|
||||
return value + '';
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo('{{str bar.baz}}', [{}, helpers], 'undefined');
|
||||
expectTemplate('{{str bar.baz}}')
|
||||
.withHelpers({
|
||||
str: function(value) {
|
||||
return value + '';
|
||||
}
|
||||
})
|
||||
.toCompileTo('undefined');
|
||||
});
|
||||
|
||||
it('GH-926: Depths and de-dupe', function() {
|
||||
var context = {
|
||||
name: 'foo',
|
||||
data: [1],
|
||||
notData: [1]
|
||||
};
|
||||
|
||||
var template = CompilerContext.compile(
|
||||
expectTemplate(
|
||||
'{{#if dater}}{{#each data}}{{../name}}{{/each}}{{else}}{{#each notData}}{{../name}}{{/each}}{{/if}}'
|
||||
);
|
||||
|
||||
var result = template(context);
|
||||
equals(result, 'foo');
|
||||
)
|
||||
.withInput({
|
||||
name: 'foo',
|
||||
data: [1],
|
||||
notData: [1]
|
||||
})
|
||||
.toCompileTo('foo');
|
||||
});
|
||||
|
||||
it('GH-1021: Each empty string key', function() {
|
||||
var data = {
|
||||
'': 'foo',
|
||||
name: 'Chris',
|
||||
value: 10000
|
||||
};
|
||||
|
||||
shouldCompileTo(
|
||||
'{{#each data}}Key: {{@key}}\n{{/each}}',
|
||||
{ data: data },
|
||||
'Key: \nKey: name\nKey: value\n'
|
||||
);
|
||||
expectTemplate('{{#each data}}Key: {{@key}}\n{{/each}}')
|
||||
.withInput({
|
||||
data: {
|
||||
'': 'foo',
|
||||
name: 'Chris',
|
||||
value: 10000
|
||||
}
|
||||
})
|
||||
.toCompileTo('Key: \nKey: name\nKey: value\n');
|
||||
});
|
||||
|
||||
it('GH-1054: Should handle simple safe string responses', function() {
|
||||
var root = '{{#wrap}}{{>partial}}{{/wrap}}';
|
||||
var partials = {
|
||||
partial: '{{#wrap}}<partial>{{/wrap}}'
|
||||
};
|
||||
var helpers = {
|
||||
wrap: function(options) {
|
||||
return new Handlebars.SafeString(options.fn());
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileToWithPartials(
|
||||
root,
|
||||
[{}, helpers, partials],
|
||||
true,
|
||||
'<partial>'
|
||||
);
|
||||
expectTemplate('{{#wrap}}{{>partial}}{{/wrap}}')
|
||||
.withHelpers({
|
||||
wrap: function(options) {
|
||||
return new Handlebars.SafeString(options.fn());
|
||||
}
|
||||
})
|
||||
.withPartials({
|
||||
partial: '{{#wrap}}<partial>{{/wrap}}'
|
||||
})
|
||||
.toCompileTo('<partial>');
|
||||
});
|
||||
|
||||
it('GH-1065: Sparse arrays', function() {
|
||||
var array = [];
|
||||
array[1] = 'foo';
|
||||
array[3] = 'bar';
|
||||
shouldCompileTo(
|
||||
'{{#each array}}{{@index}}{{.}}{{/each}}',
|
||||
{ array: array },
|
||||
'1foo3bar'
|
||||
);
|
||||
expectTemplate('{{#each array}}{{@index}}{{.}}{{/each}}')
|
||||
.withInput({ array: array })
|
||||
.toCompileTo('1foo3bar');
|
||||
});
|
||||
|
||||
it('GH-1093: Undefined helper context', function() {
|
||||
var obj = { foo: undefined, bar: 'bat' };
|
||||
var helpers = {
|
||||
helper: function() {
|
||||
// It's valid to execute a block against an undefined context, but
|
||||
// helpers can not do so, so we expect to have an empty object here;
|
||||
for (var name in this) {
|
||||
if (Object.prototype.hasOwnProperty.call(this, name)) {
|
||||
return 'found';
|
||||
expectTemplate('{{#each obj}}{{{helper}}}{{.}}{{/each}}')
|
||||
.withInput({ obj: { foo: undefined, bar: 'bat' } })
|
||||
.withHelpers({
|
||||
helper: function() {
|
||||
// It's valid to execute a block against an undefined context, but
|
||||
// helpers can not do so, so we expect to have an empty object here;
|
||||
for (var name in this) {
|
||||
if (Object.prototype.hasOwnProperty.call(this, name)) {
|
||||
return 'found';
|
||||
}
|
||||
}
|
||||
// And to make IE happy, check for the known string as length is not enumerated.
|
||||
return this === 'bat' ? 'found' : 'not';
|
||||
}
|
||||
// And to make IE happy, check for the known string as length is not enumerated.
|
||||
return this === 'bat' ? 'found' : 'not';
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo(
|
||||
'{{#each obj}}{{{helper}}}{{.}}{{/each}}',
|
||||
[{ obj: obj }, helpers],
|
||||
'notfoundbat'
|
||||
);
|
||||
})
|
||||
.toCompileTo('notfoundbat');
|
||||
});
|
||||
|
||||
it('should support multiple levels of inline partials', function() {
|
||||
var string =
|
||||
'{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}';
|
||||
var partials = {
|
||||
doctype: 'doctype{{> content}}',
|
||||
layout:
|
||||
'{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}'
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[{}, {}, partials],
|
||||
true,
|
||||
'doctypelayoutsubcontent'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#> layout}}{{#*inline "subcontent"}}subcontent{{/inline}}{{/layout}}'
|
||||
)
|
||||
.withPartials({
|
||||
doctype: 'doctype{{> content}}',
|
||||
layout:
|
||||
'{{#> doctype}}{{#*inline "content"}}layout{{> subcontent}}{{/inline}}{{/doctype}}'
|
||||
})
|
||||
.toCompileTo('doctypelayoutsubcontent');
|
||||
});
|
||||
|
||||
it('GH-1089: should support failover content in multiple levels of inline partials', function() {
|
||||
var string = '{{#> layout}}{{/layout}}';
|
||||
var partials = {
|
||||
doctype: 'doctype{{> content}}',
|
||||
layout:
|
||||
'{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}'
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[{}, {}, partials],
|
||||
true,
|
||||
'doctypelayoutsubcontent'
|
||||
);
|
||||
expectTemplate('{{#> layout}}{{/layout}}')
|
||||
.withPartials({
|
||||
doctype: 'doctype{{> content}}',
|
||||
layout:
|
||||
'{{#> doctype}}{{#*inline "content"}}layout{{#> subcontent}}subcontent{{/subcontent}}{{/inline}}{{/doctype}}'
|
||||
})
|
||||
.toCompileTo('doctypelayoutsubcontent');
|
||||
});
|
||||
|
||||
it('GH-1099: should support greater than 3 nested levels of inline partials', function() {
|
||||
var string = '{{#> layout}}Outer{{/layout}}';
|
||||
var partials = {
|
||||
layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}',
|
||||
inner: ''
|
||||
};
|
||||
shouldCompileToWithPartials(string, [{}, {}, partials], true, 'Outer');
|
||||
expectTemplate('{{#> layout}}Outer{{/layout}}')
|
||||
.withPartials({
|
||||
layout: '{{#> inner}}Inner{{/inner}}{{> @partial-block }}',
|
||||
inner: ''
|
||||
})
|
||||
.toCompileTo('Outer');
|
||||
});
|
||||
|
||||
it('GH-1135 : Context handling within each iteration', function() {
|
||||
var obj = { array: [1], name: 'John' };
|
||||
var helpers = {
|
||||
myif: function(conditional, options) {
|
||||
if (conditional) {
|
||||
return options.fn(this);
|
||||
} else {
|
||||
return options.inverse(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo(
|
||||
expectTemplate(
|
||||
'{{#each array}}\n' +
|
||||
' 1. IF: {{#if true}}{{../name}}-{{../../name}}-{{../../../name}}{{/if}}\n' +
|
||||
' 2. MYIF: {{#myif true}}{{../name}}={{../../name}}={{../../../name}}{{/myif}}\n' +
|
||||
'{{/each}}',
|
||||
[obj, helpers],
|
||||
' 1. IF: John--\n' + ' 2. MYIF: John==\n'
|
||||
);
|
||||
'{{/each}}'
|
||||
)
|
||||
.withInput({ array: [1], name: 'John' })
|
||||
.withHelpers({
|
||||
myif: function(conditional, options) {
|
||||
if (conditional) {
|
||||
return options.fn(this);
|
||||
} else {
|
||||
return options.inverse(this);
|
||||
}
|
||||
}
|
||||
})
|
||||
.toCompileTo(' 1. IF: John--\n' + ' 2. MYIF: John==\n');
|
||||
});
|
||||
|
||||
it('GH-1186: Support block params for existing programs', function() {
|
||||
var string =
|
||||
expectTemplate(
|
||||
'{{#*inline "test"}}{{> @partial-block }}{{/inline}}' +
|
||||
'{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' +
|
||||
'{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}';
|
||||
|
||||
shouldCompileTo(string, { listOne: ['a'], listTwo: ['b'] }, 'ab', '');
|
||||
'{{#>test }}{{#each listOne as |item|}}{{ item }}{{/each}}{{/test}}' +
|
||||
'{{#>test }}{{#each listTwo as |item|}}{{ item }}{{/each}}{{/test}}'
|
||||
)
|
||||
.withInput({
|
||||
listOne: ['a'],
|
||||
listTwo: ['b']
|
||||
})
|
||||
.withMessage('')
|
||||
.toCompileTo('ab');
|
||||
});
|
||||
|
||||
it('GH-1319: "unless" breaks when "each" value equals "null"', function() {
|
||||
var string =
|
||||
'{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}';
|
||||
shouldCompileTo(
|
||||
string,
|
||||
{ value: 'parent', list: [null, 'a'] },
|
||||
'parent=parent parent=parent ',
|
||||
''
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#each list}}{{#unless ./prop}}parent={{../value}} {{/unless}}{{/each}}'
|
||||
)
|
||||
.withInput({
|
||||
value: 'parent',
|
||||
list: [null, 'a']
|
||||
})
|
||||
.withMessage('')
|
||||
.toCompileTo('parent=parent parent=parent ');
|
||||
});
|
||||
|
||||
it('GH-1341: 4.0.7 release breaks {{#if @partial-block}} usage', function() {
|
||||
var string = 'template {{>partial}} template';
|
||||
var partials = {
|
||||
partialWithBlock:
|
||||
'{{#if @partial-block}} block {{> @partial-block}} block {{/if}}',
|
||||
partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}'
|
||||
};
|
||||
shouldCompileToWithPartials(
|
||||
string,
|
||||
[{}, {}, partials],
|
||||
true,
|
||||
'template block partial block template'
|
||||
);
|
||||
expectTemplate('template {{>partial}} template')
|
||||
.withPartials({
|
||||
partialWithBlock:
|
||||
'{{#if @partial-block}} block {{> @partial-block}} block {{/if}}',
|
||||
partial: '{{#> partialWithBlock}} partial {{/partialWithBlock}}'
|
||||
})
|
||||
.toCompileTo('template block partial block template');
|
||||
});
|
||||
|
||||
describe('GH-1561: 4.3.x should still work with precompiled templates from 4.0.0 <= x < 4.3.0', function() {
|
||||
@@ -482,14 +455,14 @@ describe('Regressions', function() {
|
||||
});
|
||||
|
||||
it('should allow hash with protected array names', function() {
|
||||
var obj = { array: [1], name: 'John' };
|
||||
var helpers = {
|
||||
helpa: function(options) {
|
||||
return options.hash.length;
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo('{{helpa length="foo"}}', [obj, helpers], 'foo');
|
||||
expectTemplate('{{helpa length="foo"}}')
|
||||
.withInput({ array: [1], name: 'John' })
|
||||
.withHelpers({
|
||||
helpa: function(options) {
|
||||
return options.hash.length;
|
||||
}
|
||||
})
|
||||
.toCompileTo('foo');
|
||||
});
|
||||
|
||||
describe('GH-1598: Performance degradation for partials since v4.3.0', function() {
|
||||
|
||||
+48
-44
@@ -20,36 +20,19 @@ describe('security issues', function() {
|
||||
});
|
||||
|
||||
it('should allow the "constructor" property to be accessed if it is an "ownProperty"', function() {
|
||||
shouldCompileTo(
|
||||
'{{constructor.name}}',
|
||||
{
|
||||
constructor: {
|
||||
name: 'here we go'
|
||||
}
|
||||
},
|
||||
'here we go'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'{{lookup (lookup this "constructor") "name"}}',
|
||||
{
|
||||
constructor: {
|
||||
name: 'here we go'
|
||||
}
|
||||
},
|
||||
'here we go'
|
||||
);
|
||||
expectTemplate('{{constructor.name}}')
|
||||
.withInput({ constructor: { name: 'here we go' } })
|
||||
.toCompileTo('here we go');
|
||||
|
||||
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
|
||||
.withInput({ constructor: { name: 'here we go' } })
|
||||
.toCompileTo('here we go');
|
||||
});
|
||||
|
||||
it('should allow the "constructor" property to be accessed if it is an "own property"', function() {
|
||||
shouldCompileTo(
|
||||
'{{lookup (lookup this "constructor") "name"}}',
|
||||
{
|
||||
constructor: {
|
||||
name: 'here we go'
|
||||
}
|
||||
},
|
||||
'here we go'
|
||||
);
|
||||
expectTemplate('{{lookup (lookup this "constructor") "name"}}')
|
||||
.withInput({ constructor: { name: 'here we go' } })
|
||||
.toCompileTo('here we go');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,19 +43,13 @@ describe('security issues', function() {
|
||||
|
||||
describe('without the option "allowExplicitCallOfHelperMissing"', function() {
|
||||
it('should throw an exception when calling "{{helperMissing}}" ', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile('{{helperMissing}}');
|
||||
template({});
|
||||
}, Error);
|
||||
expectTemplate('{{helperMissing}}').toThrow(Error);
|
||||
});
|
||||
|
||||
it('should throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile(
|
||||
'{{#helperMissing}}{{/helperMissing}}'
|
||||
);
|
||||
template({});
|
||||
}, Error);
|
||||
expectTemplate('{{#helperMissing}}{{/helperMissing}}').toThrow(Error);
|
||||
});
|
||||
|
||||
it('should throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
||||
var functionCalls = [];
|
||||
expect(function() {
|
||||
@@ -85,17 +62,15 @@ describe('security issues', function() {
|
||||
}).to.throw(Error);
|
||||
expect(functionCalls.length).to.equal(0);
|
||||
});
|
||||
|
||||
it('should throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
||||
shouldThrow(function() {
|
||||
var template = Handlebars.compile(
|
||||
'{{#blockHelperMissing .}}{{/blockHelperMissing}}'
|
||||
);
|
||||
template({
|
||||
expectTemplate('{{#blockHelperMissing .}}{{/blockHelperMissing}}')
|
||||
.withInput({
|
||||
fn: function() {
|
||||
return 'functionInData';
|
||||
}
|
||||
});
|
||||
}, Error);
|
||||
})
|
||||
.toThrow(Error);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -104,12 +79,14 @@ describe('security issues', function() {
|
||||
var template = Handlebars.compile('{{helperMissing}}');
|
||||
template({}, { allowCallsToHelperMissing: true });
|
||||
});
|
||||
|
||||
it('should not throw an exception when calling "{{#helperMissing}}{{/helperMissing}}" ', function() {
|
||||
var template = Handlebars.compile(
|
||||
'{{#helperMissing}}{{/helperMissing}}'
|
||||
);
|
||||
template({}, { allowCallsToHelperMissing: true });
|
||||
});
|
||||
|
||||
it('should not throw an exception when calling "{{blockHelperMissing "abc" .}}" ', function() {
|
||||
var functionCalls = [];
|
||||
var template = Handlebars.compile('{{blockHelperMissing "abc" .}}');
|
||||
@@ -123,6 +100,7 @@ describe('security issues', function() {
|
||||
);
|
||||
equals(functionCalls.length, 1);
|
||||
});
|
||||
|
||||
it('should not throw an exception when calling "{{#blockHelperMissing .}}{{/blockHelperMissing}}"', function() {
|
||||
var template = Handlebars.compile(
|
||||
'{{#blockHelperMissing true}}sdads{{/blockHelperMissing}}'
|
||||
@@ -320,6 +298,10 @@ describe('security issues', function() {
|
||||
checkProtoPropertyAccess({ compat: true });
|
||||
});
|
||||
|
||||
describe('in strict-mode', function() {
|
||||
checkProtoPropertyAccess({ strict: true });
|
||||
});
|
||||
|
||||
function checkProtoPropertyAccess(compileOptions) {
|
||||
it('should be prohibited by default and log a warning', function() {
|
||||
var spy = sinon.spy(console, 'error');
|
||||
@@ -418,6 +400,28 @@ describe('security issues', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('escapes template variables', function() {
|
||||
it('in compat mode', function() {
|
||||
expectTemplate("{{'a\\b'}}")
|
||||
.withCompileOptions({ compat: true })
|
||||
.withInput({ 'a\\b': 'c' })
|
||||
.toCompileTo('c');
|
||||
});
|
||||
|
||||
it('in default mode', function() {
|
||||
expectTemplate("{{'a\\b'}}")
|
||||
.withCompileOptions()
|
||||
.withInput({ 'a\\b': 'c' })
|
||||
.toCompileTo('c');
|
||||
});
|
||||
it('in default mode', function() {
|
||||
expectTemplate("{{'a\\b'}}")
|
||||
.withCompileOptions({ strict: true })
|
||||
.withInput({ 'a\\b': 'c' })
|
||||
.toCompileTo('c');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function wrapToAdjustContainer(precompiledTemplateFunction) {
|
||||
|
||||
+6
-16
@@ -40,22 +40,12 @@ describe('spec', function() {
|
||||
/* eslint-enable no-eval */
|
||||
}
|
||||
it(name + ' - ' + test.name, function() {
|
||||
if (test.partials) {
|
||||
shouldCompileToWithPartials(
|
||||
test.template,
|
||||
[data, {}, test.partials, true],
|
||||
true,
|
||||
test.expected,
|
||||
test.desc + ' "' + test.template + '"'
|
||||
);
|
||||
} else {
|
||||
shouldCompileTo(
|
||||
test.template,
|
||||
[data, {}, {}, true],
|
||||
test.expected,
|
||||
test.desc + ' "' + test.template + '"'
|
||||
);
|
||||
}
|
||||
expectTemplate(test.template)
|
||||
.withInput(data)
|
||||
.withPartials(test.partials || {})
|
||||
.withCompileOptions({ compat: true })
|
||||
.withMessage(test.desc + ' "' + test.template + '"')
|
||||
.toCompileTo(test.expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+95
-161
@@ -3,161 +3,107 @@ var Exception = Handlebars.Exception;
|
||||
describe('strict', function() {
|
||||
describe('strict mode', function() {
|
||||
it('should error on missing property lookup', function() {
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{hello}}', { strict: true });
|
||||
|
||||
template({});
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
expectTemplate('{{hello}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.toThrow(Exception, /"hello" not defined in/);
|
||||
});
|
||||
|
||||
it('should error on missing child', function() {
|
||||
var template = CompilerContext.compile('{{hello.bar}}', { strict: true });
|
||||
equals(template({ hello: { bar: 'foo' } }), 'foo');
|
||||
expectTemplate('{{hello.bar}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.withInput({ hello: { bar: 'foo' } })
|
||||
.toCompileTo('foo');
|
||||
|
||||
shouldThrow(
|
||||
function() {
|
||||
template({ hello: {} });
|
||||
},
|
||||
Exception,
|
||||
/"bar" not defined in/
|
||||
);
|
||||
expectTemplate('{{hello.bar}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.withInput({ hello: {} })
|
||||
.toThrow(Exception, /"bar" not defined in/);
|
||||
});
|
||||
|
||||
it('should handle explicit undefined', function() {
|
||||
var template = CompilerContext.compile('{{hello.bar}}', { strict: true });
|
||||
|
||||
equals(template({ hello: { bar: undefined } }), '');
|
||||
expectTemplate('{{hello.bar}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.withInput({ hello: { bar: undefined } })
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('should error on missing property lookup in known helpers mode', function() {
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{hello}}', {
|
||||
strict: true,
|
||||
knownHelpersOnly: true
|
||||
});
|
||||
|
||||
template({});
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
expectTemplate('{{hello}}')
|
||||
.withCompileOptions({
|
||||
strict: true,
|
||||
knownHelpersOnly: true
|
||||
})
|
||||
.toThrow(Exception, /"hello" not defined in/);
|
||||
});
|
||||
it('should error on missing context', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{hello}}', { strict: true });
|
||||
|
||||
template();
|
||||
}, Error);
|
||||
it('should error on missing context', function() {
|
||||
expectTemplate('{{hello}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.toThrow(Error);
|
||||
});
|
||||
|
||||
it('should error on missing data lookup', function() {
|
||||
var template = CompilerContext.compile('{{@hello}}', { strict: true });
|
||||
equals(template(undefined, { data: { hello: 'foo' } }), 'foo');
|
||||
var xt = expectTemplate('{{@hello}}').withCompileOptions({
|
||||
strict: true
|
||||
});
|
||||
|
||||
shouldThrow(function() {
|
||||
template();
|
||||
}, Error);
|
||||
xt.toThrow(Error);
|
||||
|
||||
xt.withRuntimeOptions({ data: { hello: 'foo' } }).toCompileTo('foo');
|
||||
});
|
||||
|
||||
it('should not run helperMissing for helper calls', function() {
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{hello foo}}', {
|
||||
strict: true
|
||||
});
|
||||
expectTemplate('{{hello foo}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.withInput({ foo: true })
|
||||
.toThrow(Exception, /"hello" not defined in/);
|
||||
|
||||
template({ foo: true });
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{#hello foo}}{{/hello}}', {
|
||||
strict: true
|
||||
});
|
||||
|
||||
template({ foo: true });
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
expectTemplate('{{#hello foo}}{{/hello}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.withInput({ foo: true })
|
||||
.toThrow(Exception, /"hello" not defined in/);
|
||||
});
|
||||
|
||||
it('should throw on ambiguous blocks', function() {
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{#hello}}{{/hello}}', {
|
||||
strict: true
|
||||
});
|
||||
expectTemplate('{{#hello}}{{/hello}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.toThrow(Exception, /"hello" not defined in/);
|
||||
|
||||
template({});
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
expectTemplate('{{^hello}}{{/hello}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.toThrow(Exception, /"hello" not defined in/);
|
||||
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('{{^hello}}{{/hello}}', {
|
||||
strict: true
|
||||
});
|
||||
|
||||
template({});
|
||||
},
|
||||
Exception,
|
||||
/"hello" not defined in/
|
||||
);
|
||||
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#hello.bar}}{{/hello.bar}}',
|
||||
{ strict: true }
|
||||
);
|
||||
|
||||
template({ hello: {} });
|
||||
},
|
||||
Exception,
|
||||
/"bar" not defined in/
|
||||
);
|
||||
expectTemplate('{{#hello.bar}}{{/hello.bar}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.withInput({ hello: {} })
|
||||
.toThrow(Exception, /"bar" not defined in/);
|
||||
});
|
||||
|
||||
it('should allow undefined parameters when passed to helpers', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#unless foo}}success{{/unless}}',
|
||||
{ strict: true }
|
||||
);
|
||||
equals(template({}), 'success');
|
||||
expectTemplate('{{#unless foo}}success{{/unless}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should allow undefined hash when passed to helpers', function() {
|
||||
var template = CompilerContext.compile('{{helper value=@foo}}', {
|
||||
strict: true
|
||||
});
|
||||
var helpers = {
|
||||
helper: function(options) {
|
||||
equals('value' in options.hash, true);
|
||||
equals(options.hash.value, undefined);
|
||||
return 'success';
|
||||
}
|
||||
};
|
||||
equals(template({}, { helpers: helpers }), 'success');
|
||||
expectTemplate('{{helper value=@foo}}')
|
||||
.withCompileOptions({
|
||||
strict: true
|
||||
})
|
||||
.withHelpers({
|
||||
helper: function(options) {
|
||||
equals('value' in options.hash, true);
|
||||
equals(options.hash.value, undefined);
|
||||
return 'success';
|
||||
}
|
||||
})
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should show error location on missing property lookup', function() {
|
||||
shouldThrow(
|
||||
function() {
|
||||
var template = CompilerContext.compile('\n\n\n {{hello}}', {
|
||||
strict: true
|
||||
});
|
||||
template({});
|
||||
},
|
||||
Exception,
|
||||
'"hello" not defined in [object Object] - 4:5'
|
||||
);
|
||||
expectTemplate('\n\n\n {{hello}}')
|
||||
.withCompileOptions({ strict: true })
|
||||
.toThrow(Exception, '"hello" not defined in [object Object] - 4:5');
|
||||
});
|
||||
|
||||
it('should error contains correct location properties on missing property lookup', function() {
|
||||
@@ -177,54 +123,42 @@ describe('strict', function() {
|
||||
|
||||
describe('assume objects', function() {
|
||||
it('should ignore missing property', function() {
|
||||
var template = CompilerContext.compile('{{hello}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
equal(template({}), '');
|
||||
expectTemplate('{{hello}}')
|
||||
.withCompileOptions({ assumeObjects: true })
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('should ignore missing child', function() {
|
||||
var template = CompilerContext.compile('{{hello.bar}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
equal(template({ hello: {} }), '');
|
||||
expectTemplate('{{hello.bar}}')
|
||||
.withCompileOptions({ assumeObjects: true })
|
||||
.withInput({ hello: {} })
|
||||
.toCompileTo('');
|
||||
});
|
||||
|
||||
it('should error on missing object', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{hello.bar}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
template({});
|
||||
}, Error);
|
||||
expectTemplate('{{hello.bar}}')
|
||||
.withCompileOptions({ assumeObjects: true })
|
||||
.toThrow(Error);
|
||||
});
|
||||
it('should error on missing context', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{hello}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
template();
|
||||
}, Error);
|
||||
it('should error on missing context', function() {
|
||||
expectTemplate('{{hello}}')
|
||||
.withCompileOptions({ assumeObjects: true })
|
||||
.withInput(undefined)
|
||||
.toThrow(Error);
|
||||
});
|
||||
|
||||
it('should error on missing data lookup', function() {
|
||||
shouldThrow(function() {
|
||||
var template = CompilerContext.compile('{{@hello.bar}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
template();
|
||||
}, Error);
|
||||
expectTemplate('{{@hello.bar}}')
|
||||
.withCompileOptions({ assumeObjects: true })
|
||||
.withInput(undefined)
|
||||
.toThrow(Error);
|
||||
});
|
||||
|
||||
it('should execute blockHelperMissing', function() {
|
||||
var template = CompilerContext.compile('{{^hello}}foo{{/hello}}', {
|
||||
assumeObjects: true
|
||||
});
|
||||
|
||||
equals(template({}), 'foo');
|
||||
expectTemplate('{{^hello}}foo{{/hello}}')
|
||||
.withCompileOptions({ assumeObjects: true })
|
||||
.toCompileTo('foo');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+152
-191
@@ -1,151 +1,117 @@
|
||||
describe('string params mode', function() {
|
||||
it('arguments to helpers can be retrieved from options hash in string form', function() {
|
||||
var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {
|
||||
stringParams: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun) {
|
||||
return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun;
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers });
|
||||
|
||||
equals(
|
||||
result,
|
||||
'HELP ME MY BOSS is.a slave.driver',
|
||||
'String parameters output'
|
||||
);
|
||||
expectTemplate('{{wycats is.a slave.driver}}')
|
||||
.withCompileOptions({
|
||||
stringParams: true
|
||||
})
|
||||
.withHelpers({
|
||||
wycats: function(passiveVoice, noun) {
|
||||
return 'HELP ME MY BOSS ' + passiveVoice + ' ' + noun;
|
||||
}
|
||||
})
|
||||
.withMessage('String parameters output')
|
||||
.toCompileTo('HELP ME MY BOSS is.a slave.driver');
|
||||
});
|
||||
|
||||
it('when using block form, arguments to helpers can be retrieved from options hash in string form', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#wycats is.a slave.driver}}help :({{/wycats}}',
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
noun +
|
||||
': ' +
|
||||
options.fn(this)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers });
|
||||
|
||||
equals(
|
||||
result,
|
||||
'HELP ME MY BOSS is.a slave.driver: help :(',
|
||||
'String parameters output'
|
||||
);
|
||||
expectTemplate('{{#wycats is.a slave.driver}}help :({{/wycats}}')
|
||||
.withCompileOptions({
|
||||
stringParams: true
|
||||
})
|
||||
.withHelpers({
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
noun +
|
||||
': ' +
|
||||
options.fn(this)
|
||||
);
|
||||
}
|
||||
})
|
||||
.withMessage('String parameters output')
|
||||
.toCompileTo('HELP ME MY BOSS is.a slave.driver: help :(');
|
||||
});
|
||||
|
||||
it('when inside a block in String mode, .. passes the appropriate context in the options hash', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}',
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
tomdale: function(desire, noun, options) {
|
||||
return (
|
||||
'STOP ME FROM READING HACKER NEWS I ' +
|
||||
options.contexts[0][desire] +
|
||||
' ' +
|
||||
noun
|
||||
);
|
||||
},
|
||||
|
||||
with: function(context, options) {
|
||||
return options.fn(options.contexts[0][context]);
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{
|
||||
expectTemplate('{{#with dale}}{{tomdale ../need dad.joke}}{{/with}}')
|
||||
.withCompileOptions({
|
||||
stringParams: true
|
||||
})
|
||||
.withHelpers({
|
||||
tomdale: function(desire, noun, options) {
|
||||
return (
|
||||
'STOP ME FROM READING HACKER NEWS I ' +
|
||||
options.contexts[0][desire] +
|
||||
' ' +
|
||||
noun
|
||||
);
|
||||
},
|
||||
with: function(context, options) {
|
||||
return options.fn(options.contexts[0][context]);
|
||||
}
|
||||
})
|
||||
.withInput({
|
||||
dale: {},
|
||||
|
||||
need: 'need-a'
|
||||
},
|
||||
{ helpers: helpers }
|
||||
);
|
||||
|
||||
equals(
|
||||
result,
|
||||
'STOP ME FROM READING HACKER NEWS I need-a dad.joke',
|
||||
'Proper context variable output'
|
||||
);
|
||||
})
|
||||
.withMessage('Proper context variable output')
|
||||
.toCompileTo('STOP ME FROM READING HACKER NEWS I need-a dad.joke');
|
||||
});
|
||||
|
||||
it('information about the types is passed along', function() {
|
||||
var template = CompilerContext.compile(
|
||||
"{{tomdale 'need' dad.joke true false}}",
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
tomdale: function(desire, noun, trueBool, falseBool, options) {
|
||||
equal(options.types[0], 'StringLiteral', 'the string type is passed');
|
||||
equal(
|
||||
options.types[1],
|
||||
'PathExpression',
|
||||
'the expression type is passed'
|
||||
);
|
||||
equal(
|
||||
options.types[2],
|
||||
'BooleanLiteral',
|
||||
'the expression type is passed'
|
||||
);
|
||||
equal(desire, 'need', 'the string form is passed for strings');
|
||||
equal(noun, 'dad.joke', 'the string form is passed for expressions');
|
||||
equal(trueBool, true, 'raw booleans are passed through');
|
||||
equal(falseBool, false, 'raw booleans are passed through');
|
||||
return 'Helper called';
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers });
|
||||
equal(result, 'Helper called');
|
||||
expectTemplate("{{tomdale 'need' dad.joke true false}}")
|
||||
.withCompileOptions({
|
||||
stringParams: true
|
||||
})
|
||||
.withHelpers({
|
||||
tomdale: function(desire, noun, trueBool, falseBool, options) {
|
||||
equal(options.types[0], 'StringLiteral', 'the string type is passed');
|
||||
equal(
|
||||
options.types[1],
|
||||
'PathExpression',
|
||||
'the expression type is passed'
|
||||
);
|
||||
equal(
|
||||
options.types[2],
|
||||
'BooleanLiteral',
|
||||
'the expression type is passed'
|
||||
);
|
||||
equal(desire, 'need', 'the string form is passed for strings');
|
||||
equal(noun, 'dad.joke', 'the string form is passed for expressions');
|
||||
equal(trueBool, true, 'raw booleans are passed through');
|
||||
equal(falseBool, false, 'raw booleans are passed through');
|
||||
return 'Helper called';
|
||||
}
|
||||
})
|
||||
.toCompileTo('Helper called');
|
||||
});
|
||||
|
||||
it('hash parameters get type information', function() {
|
||||
var template = CompilerContext.compile(
|
||||
"{{tomdale he.says desire='need' noun=dad.joke bool=true}}",
|
||||
{ stringParams: true }
|
||||
);
|
||||
expectTemplate("{{tomdale he.says desire='need' noun=dad.joke bool=true}}")
|
||||
.withCompileOptions({
|
||||
stringParams: true
|
||||
})
|
||||
.withHelpers({
|
||||
tomdale: function(exclamation, options) {
|
||||
equal(exclamation, 'he.says');
|
||||
equal(options.types[0], 'PathExpression');
|
||||
|
||||
var helpers = {
|
||||
tomdale: function(exclamation, options) {
|
||||
equal(exclamation, 'he.says');
|
||||
equal(options.types[0], 'PathExpression');
|
||||
|
||||
equal(options.hashTypes.desire, 'StringLiteral');
|
||||
equal(options.hashTypes.noun, 'PathExpression');
|
||||
equal(options.hashTypes.bool, 'BooleanLiteral');
|
||||
equal(options.hash.desire, 'need');
|
||||
equal(options.hash.noun, 'dad.joke');
|
||||
equal(options.hash.bool, true);
|
||||
return 'Helper called';
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers });
|
||||
equal(result, 'Helper called');
|
||||
equal(options.hashTypes.desire, 'StringLiteral');
|
||||
equal(options.hashTypes.noun, 'PathExpression');
|
||||
equal(options.hashTypes.bool, 'BooleanLiteral');
|
||||
equal(options.hash.desire, 'need');
|
||||
equal(options.hash.noun, 'dad.joke');
|
||||
equal(options.hash.bool, true);
|
||||
return 'Helper called';
|
||||
}
|
||||
})
|
||||
.toCompileTo('Helper called');
|
||||
});
|
||||
|
||||
it('hash parameters get context information', function() {
|
||||
var template = CompilerContext.compile(
|
||||
"{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}",
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var context = { dale: {} };
|
||||
|
||||
var helpers = {
|
||||
@@ -165,82 +131,77 @@ describe('string params mode', function() {
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(context, { helpers: helpers });
|
||||
equal(result, 'Helper called');
|
||||
expectTemplate(
|
||||
"{{#with dale}}{{tomdale he.says desire='need' noun=../dad/joke bool=true}}{{/with}}"
|
||||
)
|
||||
.withCompileOptions({ stringParams: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput(context)
|
||||
.toCompileTo('Helper called');
|
||||
});
|
||||
|
||||
it('when inside a block in String mode, .. passes the appropriate context in the options hash to a block helper', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}',
|
||||
{ stringParams: true }
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#with dale}}{{#tomdale ../need dad.joke}}wot{{/tomdale}}{{/with}}'
|
||||
)
|
||||
.withCompileOptions({
|
||||
stringParams: true
|
||||
})
|
||||
.withHelpers({
|
||||
tomdale: function(desire, noun, options) {
|
||||
return (
|
||||
'STOP ME FROM READING HACKER NEWS I ' +
|
||||
options.contexts[0][desire] +
|
||||
' ' +
|
||||
noun +
|
||||
' ' +
|
||||
options.fn(this)
|
||||
);
|
||||
},
|
||||
|
||||
var helpers = {
|
||||
tomdale: function(desire, noun, options) {
|
||||
return (
|
||||
'STOP ME FROM READING HACKER NEWS I ' +
|
||||
options.contexts[0][desire] +
|
||||
' ' +
|
||||
noun +
|
||||
' ' +
|
||||
options.fn(this)
|
||||
);
|
||||
},
|
||||
|
||||
with: function(context, options) {
|
||||
return options.fn(options.contexts[0][context]);
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{
|
||||
with: function(context, options) {
|
||||
return options.fn(options.contexts[0][context]);
|
||||
}
|
||||
})
|
||||
.withInput({
|
||||
dale: {},
|
||||
|
||||
need: 'need-a'
|
||||
},
|
||||
{ helpers: helpers }
|
||||
);
|
||||
|
||||
equals(
|
||||
result,
|
||||
'STOP ME FROM READING HACKER NEWS I need-a dad.joke wot',
|
||||
'Proper context variable output'
|
||||
);
|
||||
})
|
||||
.withMessage('Proper context variable output')
|
||||
.toCompileTo('STOP ME FROM READING HACKER NEWS I need-a dad.joke wot');
|
||||
});
|
||||
|
||||
it('with nested block ambiguous', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}',
|
||||
{ stringParams: true }
|
||||
);
|
||||
|
||||
var helpers = {
|
||||
with: function() {
|
||||
return 'WITH';
|
||||
},
|
||||
view: function() {
|
||||
return 'VIEW';
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers });
|
||||
equals(result, 'WITH');
|
||||
expectTemplate(
|
||||
'{{#with content}}{{#view}}{{firstName}} {{lastName}}{{/view}}{{/with}}'
|
||||
)
|
||||
.withCompileOptions({
|
||||
stringParams: true
|
||||
})
|
||||
.withHelpers({
|
||||
with: function() {
|
||||
return 'WITH';
|
||||
},
|
||||
view: function() {
|
||||
return 'VIEW';
|
||||
}
|
||||
})
|
||||
.toCompileTo('WITH');
|
||||
});
|
||||
|
||||
it('should handle DATA', function() {
|
||||
var template = CompilerContext.compile('{{foo @bar}}', {
|
||||
stringParams: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
foo: function(bar, options) {
|
||||
equal(bar, '@bar');
|
||||
equal(options.types[0], 'PathExpression');
|
||||
return 'Foo!';
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers });
|
||||
equal(result, 'Foo!');
|
||||
expectTemplate('{{foo @bar}}')
|
||||
.withCompileOptions({
|
||||
stringParams: true
|
||||
})
|
||||
.withHelpers({
|
||||
foo: function(bar, options) {
|
||||
equal(bar, '@bar');
|
||||
equal(options.types[0], 'PathExpression');
|
||||
return 'Foo!';
|
||||
}
|
||||
})
|
||||
.toCompileTo('Foo!');
|
||||
});
|
||||
});
|
||||
|
||||
+212
-226
@@ -1,61 +1,57 @@
|
||||
describe('subexpressions', function() {
|
||||
it('arg-less helper', function() {
|
||||
var string = '{{foo (bar)}}!';
|
||||
var context = {};
|
||||
var helpers = {
|
||||
foo: function(val) {
|
||||
return val + val;
|
||||
},
|
||||
bar: function() {
|
||||
return 'LOL';
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [context, helpers], 'LOLLOL!');
|
||||
expectTemplate('{{foo (bar)}}!')
|
||||
.withHelpers({
|
||||
foo: function(val) {
|
||||
return val + val;
|
||||
},
|
||||
bar: function() {
|
||||
return 'LOL';
|
||||
}
|
||||
})
|
||||
.toCompileTo('LOLLOL!');
|
||||
});
|
||||
|
||||
it('helper w args', function() {
|
||||
var string = '{{blog (equal a b)}}';
|
||||
|
||||
var context = { bar: 'LOL' };
|
||||
var helpers = {
|
||||
blog: function(val) {
|
||||
return 'val is ' + val;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [context, helpers], 'val is true');
|
||||
expectTemplate('{{blog (equal a b)}}')
|
||||
.withInput({ bar: 'LOL' })
|
||||
.withHelpers({
|
||||
blog: function(val) {
|
||||
return 'val is ' + val;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
})
|
||||
.toCompileTo('val is true');
|
||||
});
|
||||
|
||||
it('mixed paths and helpers', function() {
|
||||
var string = '{{blog baz.bat (equal a b) baz.bar}}';
|
||||
|
||||
var context = { bar: 'LOL', baz: { bat: 'foo!', bar: 'bar!' } };
|
||||
var helpers = {
|
||||
blog: function(val, that, theOther) {
|
||||
return 'val is ' + val + ', ' + that + ' and ' + theOther;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [context, helpers], 'val is foo!, true and bar!');
|
||||
expectTemplate('{{blog baz.bat (equal a b) baz.bar}}')
|
||||
.withInput({ bar: 'LOL', baz: { bat: 'foo!', bar: 'bar!' } })
|
||||
.withHelpers({
|
||||
blog: function(val, that, theOther) {
|
||||
return 'val is ' + val + ', ' + that + ' and ' + theOther;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
})
|
||||
.toCompileTo('val is foo!, true and bar!');
|
||||
});
|
||||
|
||||
it('supports much nesting', function() {
|
||||
var string = '{{blog (equal (equal true true) true)}}';
|
||||
|
||||
var context = { bar: 'LOL' };
|
||||
var helpers = {
|
||||
blog: function(val) {
|
||||
return 'val is ' + val;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [context, helpers], 'val is true');
|
||||
expectTemplate('{{blog (equal (equal true true) true)}}')
|
||||
.withInput({ bar: 'LOL' })
|
||||
.withHelpers({
|
||||
blog: function(val) {
|
||||
return 'val is ' + val;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
})
|
||||
.toCompileTo('val is true');
|
||||
});
|
||||
|
||||
it('GH-800 : Complex subexpressions', function() {
|
||||
@@ -69,20 +65,33 @@ describe('subexpressions', function() {
|
||||
}
|
||||
};
|
||||
|
||||
shouldCompileTo(
|
||||
"{{dash 'abc' (concat a b)}}",
|
||||
[context, helpers],
|
||||
'abc-ab'
|
||||
);
|
||||
shouldCompileTo('{{dash d (concat a b)}}', [context, helpers], 'd-ab');
|
||||
shouldCompileTo('{{dash c.c (concat a b)}}', [context, helpers], 'c-ab');
|
||||
shouldCompileTo('{{dash (concat a b) c.c}}', [context, helpers], 'ab-c');
|
||||
shouldCompileTo('{{dash (concat a e.e) c.c}}', [context, helpers], 'ae-c');
|
||||
expectTemplate("{{dash 'abc' (concat a b)}}")
|
||||
.withInput(context)
|
||||
.withHelpers(helpers)
|
||||
.toCompileTo('abc-ab');
|
||||
|
||||
expectTemplate('{{dash d (concat a b)}}')
|
||||
.withInput(context)
|
||||
.withHelpers(helpers)
|
||||
.toCompileTo('d-ab');
|
||||
|
||||
expectTemplate('{{dash c.c (concat a b)}}')
|
||||
.withInput(context)
|
||||
.withHelpers(helpers)
|
||||
.toCompileTo('c-ab');
|
||||
|
||||
expectTemplate('{{dash (concat a b) c.c}}')
|
||||
.withInput(context)
|
||||
.withHelpers(helpers)
|
||||
.toCompileTo('ab-c');
|
||||
|
||||
expectTemplate('{{dash (concat a e.e) c.c}}')
|
||||
.withInput(context)
|
||||
.withHelpers(helpers)
|
||||
.toCompileTo('ae-c');
|
||||
});
|
||||
|
||||
it('provides each nested helper invocation its own options hash', function() {
|
||||
var string = '{{equal (equal true true) true}}';
|
||||
|
||||
var lastOptions = null;
|
||||
var helpers = {
|
||||
equal: function(x, y, options) {
|
||||
@@ -93,200 +102,177 @@ describe('subexpressions', function() {
|
||||
return x === y;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [{}, helpers], 'true');
|
||||
expectTemplate('{{equal (equal true true) true}}')
|
||||
.withHelpers(helpers)
|
||||
.toCompileTo('true');
|
||||
});
|
||||
|
||||
it('with hashes', function() {
|
||||
var string = "{{blog (equal (equal true true) true fun='yes')}}";
|
||||
|
||||
var context = { bar: 'LOL' };
|
||||
var helpers = {
|
||||
blog: function(val) {
|
||||
return 'val is ' + val;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [context, helpers], 'val is true');
|
||||
expectTemplate("{{blog (equal (equal true true) true fun='yes')}}")
|
||||
.withInput({ bar: 'LOL' })
|
||||
.withHelpers({
|
||||
blog: function(val) {
|
||||
return 'val is ' + val;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
})
|
||||
.toCompileTo('val is true');
|
||||
});
|
||||
|
||||
it('as hashes', function() {
|
||||
var string = "{{blog fun=(equal (blog fun=1) 'val is 1')}}";
|
||||
|
||||
var helpers = {
|
||||
blog: function(options) {
|
||||
return 'val is ' + options.hash.fun;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [{}, helpers], 'val is true');
|
||||
expectTemplate("{{blog fun=(equal (blog fun=1) 'val is 1')}}")
|
||||
.withHelpers({
|
||||
blog: function(options) {
|
||||
return 'val is ' + options.hash.fun;
|
||||
},
|
||||
equal: function(x, y) {
|
||||
return x === y;
|
||||
}
|
||||
})
|
||||
.toCompileTo('val is true');
|
||||
});
|
||||
|
||||
it('multiple subexpressions in a hash', function() {
|
||||
var string =
|
||||
'{{input aria-label=(t "Name") placeholder=(t "Example User")}}';
|
||||
|
||||
var helpers = {
|
||||
input: function(options) {
|
||||
var hash = options.hash;
|
||||
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
|
||||
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
|
||||
return new Handlebars.SafeString(
|
||||
'<input aria-label="' +
|
||||
ariaLabel +
|
||||
'" placeholder="' +
|
||||
placeholder +
|
||||
'" />'
|
||||
);
|
||||
},
|
||||
t: function(defaultString) {
|
||||
return new Handlebars.SafeString(defaultString);
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[{}, helpers],
|
||||
'<input aria-label="Name" placeholder="Example User" />'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{input aria-label=(t "Name") placeholder=(t "Example User")}}'
|
||||
)
|
||||
.withHelpers({
|
||||
input: function(options) {
|
||||
var hash = options.hash;
|
||||
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
|
||||
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
|
||||
return new Handlebars.SafeString(
|
||||
'<input aria-label="' +
|
||||
ariaLabel +
|
||||
'" placeholder="' +
|
||||
placeholder +
|
||||
'" />'
|
||||
);
|
||||
},
|
||||
t: function(defaultString) {
|
||||
return new Handlebars.SafeString(defaultString);
|
||||
}
|
||||
})
|
||||
.toCompileTo('<input aria-label="Name" placeholder="Example User" />');
|
||||
});
|
||||
|
||||
it('multiple subexpressions in a hash with context', function() {
|
||||
var string =
|
||||
'{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}';
|
||||
|
||||
var context = {
|
||||
item: {
|
||||
field: 'Name',
|
||||
placeholder: 'Example User'
|
||||
}
|
||||
};
|
||||
|
||||
var helpers = {
|
||||
input: function(options) {
|
||||
var hash = options.hash;
|
||||
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
|
||||
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
|
||||
return new Handlebars.SafeString(
|
||||
'<input aria-label="' +
|
||||
ariaLabel +
|
||||
'" placeholder="' +
|
||||
placeholder +
|
||||
'" />'
|
||||
);
|
||||
},
|
||||
t: function(defaultString) {
|
||||
return new Handlebars.SafeString(defaultString);
|
||||
}
|
||||
};
|
||||
shouldCompileTo(
|
||||
string,
|
||||
[context, helpers],
|
||||
'<input aria-label="Name" placeholder="Example User" />'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{input aria-label=(t item.field) placeholder=(t item.placeholder)}}'
|
||||
)
|
||||
.withInput({
|
||||
item: {
|
||||
field: 'Name',
|
||||
placeholder: 'Example User'
|
||||
}
|
||||
})
|
||||
.withHelpers({
|
||||
input: function(options) {
|
||||
var hash = options.hash;
|
||||
var ariaLabel = Handlebars.Utils.escapeExpression(hash['aria-label']);
|
||||
var placeholder = Handlebars.Utils.escapeExpression(hash.placeholder);
|
||||
return new Handlebars.SafeString(
|
||||
'<input aria-label="' +
|
||||
ariaLabel +
|
||||
'" placeholder="' +
|
||||
placeholder +
|
||||
'" />'
|
||||
);
|
||||
},
|
||||
t: function(defaultString) {
|
||||
return new Handlebars.SafeString(defaultString);
|
||||
}
|
||||
})
|
||||
.toCompileTo('<input aria-label="Name" placeholder="Example User" />');
|
||||
});
|
||||
|
||||
it('in string params mode,', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{snog (blorg foo x=y) yeah a=b}}',
|
||||
{ stringParams: true }
|
||||
);
|
||||
expectTemplate('{{snog (blorg foo x=y) yeah a=b}}')
|
||||
.withCompileOptions({ stringParams: true })
|
||||
.withHelpers({
|
||||
snog: function(a, b, options) {
|
||||
equals(a, 'foo');
|
||||
equals(
|
||||
options.types.length,
|
||||
2,
|
||||
'string params for outer helper processed correctly'
|
||||
);
|
||||
equals(
|
||||
options.types[0],
|
||||
'SubExpression',
|
||||
'string params for outer helper processed correctly'
|
||||
);
|
||||
equals(
|
||||
options.types[1],
|
||||
'PathExpression',
|
||||
'string params for outer helper processed correctly'
|
||||
);
|
||||
return a + b;
|
||||
},
|
||||
|
||||
var helpers = {
|
||||
snog: function(a, b, options) {
|
||||
equals(a, 'foo');
|
||||
equals(
|
||||
options.types.length,
|
||||
2,
|
||||
'string params for outer helper processed correctly'
|
||||
);
|
||||
equals(
|
||||
options.types[0],
|
||||
'SubExpression',
|
||||
'string params for outer helper processed correctly'
|
||||
);
|
||||
equals(
|
||||
options.types[1],
|
||||
'PathExpression',
|
||||
'string params for outer helper processed correctly'
|
||||
);
|
||||
return a + b;
|
||||
},
|
||||
|
||||
blorg: function(a, options) {
|
||||
equals(
|
||||
options.types.length,
|
||||
1,
|
||||
'string params for inner helper processed correctly'
|
||||
);
|
||||
equals(
|
||||
options.types[0],
|
||||
'PathExpression',
|
||||
'string params for inner helper processed correctly'
|
||||
);
|
||||
return a;
|
||||
}
|
||||
};
|
||||
|
||||
var result = template(
|
||||
{
|
||||
blorg: function(a, options) {
|
||||
equals(
|
||||
options.types.length,
|
||||
1,
|
||||
'string params for inner helper processed correctly'
|
||||
);
|
||||
equals(
|
||||
options.types[0],
|
||||
'PathExpression',
|
||||
'string params for inner helper processed correctly'
|
||||
);
|
||||
return a;
|
||||
}
|
||||
})
|
||||
.withInput({
|
||||
foo: {},
|
||||
yeah: {}
|
||||
},
|
||||
{ helpers: helpers }
|
||||
);
|
||||
|
||||
equals(result, 'fooyeah');
|
||||
})
|
||||
.toCompileTo('fooyeah');
|
||||
});
|
||||
|
||||
it('as hashes in string params mode', function() {
|
||||
var template = CompilerContext.compile('{{blog fun=(bork)}}', {
|
||||
stringParams: true
|
||||
});
|
||||
|
||||
var helpers = {
|
||||
blog: function(options) {
|
||||
equals(options.hashTypes.fun, 'SubExpression');
|
||||
return 'val is ' + options.hash.fun;
|
||||
},
|
||||
bork: function() {
|
||||
return 'BORK';
|
||||
}
|
||||
};
|
||||
|
||||
var result = template({}, { helpers: helpers });
|
||||
equals(result, 'val is BORK');
|
||||
expectTemplate('{{blog fun=(bork)}}')
|
||||
.withCompileOptions({ stringParams: true })
|
||||
.withHelpers({
|
||||
blog: function(options) {
|
||||
equals(options.hashTypes.fun, 'SubExpression');
|
||||
return 'val is ' + options.hash.fun;
|
||||
},
|
||||
bork: function() {
|
||||
return 'BORK';
|
||||
}
|
||||
})
|
||||
.toCompileTo('val is BORK');
|
||||
});
|
||||
|
||||
it('subexpression functions on the context', function() {
|
||||
var string = '{{foo (bar)}}!';
|
||||
var context = {
|
||||
bar: function() {
|
||||
return 'LOL';
|
||||
}
|
||||
};
|
||||
var helpers = {
|
||||
foo: function(val) {
|
||||
return val + val;
|
||||
}
|
||||
};
|
||||
shouldCompileTo(string, [context, helpers], 'LOLLOL!');
|
||||
expectTemplate('{{foo (bar)}}!')
|
||||
.withInput({
|
||||
bar: function() {
|
||||
return 'LOL';
|
||||
}
|
||||
})
|
||||
.withHelpers({
|
||||
foo: function(val) {
|
||||
return val + val;
|
||||
}
|
||||
})
|
||||
.toCompileTo('LOLLOL!');
|
||||
});
|
||||
|
||||
it("subexpressions can't just be property lookups", function() {
|
||||
var string = '{{foo (bar)}}!';
|
||||
var context = {
|
||||
bar: 'LOL'
|
||||
};
|
||||
var helpers = {
|
||||
foo: function(val) {
|
||||
return val + val;
|
||||
}
|
||||
};
|
||||
shouldThrow(function() {
|
||||
shouldCompileTo(string, [context, helpers], 'LOLLOL!');
|
||||
});
|
||||
expectTemplate('{{foo (bar)}}!')
|
||||
.withInput({
|
||||
bar: 'LOL'
|
||||
})
|
||||
.withHelpers({
|
||||
foo: function(val) {
|
||||
return val + val;
|
||||
}
|
||||
})
|
||||
.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
+213
-279
@@ -5,216 +5,184 @@ describe('track ids', function() {
|
||||
});
|
||||
|
||||
it('should not include anything without the flag', function() {
|
||||
var template = CompilerContext.compile('{{wycats is.a slave.driver}}');
|
||||
expectTemplate('{{wycats is.a slave.driver}}')
|
||||
.withHelpers({
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
equal(options.ids, undefined);
|
||||
equal(options.hashIds, undefined);
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
equal(options.ids, undefined);
|
||||
equal(options.hashIds, undefined);
|
||||
|
||||
return 'success';
|
||||
}
|
||||
};
|
||||
|
||||
equals(template({}, { helpers: helpers }), 'success');
|
||||
return 'success';
|
||||
}
|
||||
})
|
||||
.toCompileTo('success');
|
||||
});
|
||||
|
||||
it('should include argument ids', function() {
|
||||
var template = CompilerContext.compile('{{wycats is.a slave.driver}}', {
|
||||
trackIds: true
|
||||
});
|
||||
expectTemplate('{{wycats is.a slave.driver}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers({
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
equal(options.ids[0], 'is.a');
|
||||
equal(options.ids[1], 'slave.driver');
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
equal(options.ids[0], 'is.a');
|
||||
equal(options.ids[1], 'slave.driver');
|
||||
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'HELP ME MY BOSS is.a:foo slave.driver:bar'
|
||||
);
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
})
|
||||
.withInput(context)
|
||||
.toCompileTo('HELP ME MY BOSS is.a:foo slave.driver:bar');
|
||||
});
|
||||
|
||||
it('should include hash ids', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{wycats bat=is.a baz=slave.driver}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
expectTemplate('{{wycats bat=is.a baz=slave.driver}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers({
|
||||
wycats: function(options) {
|
||||
equal(options.hashIds.bat, 'is.a');
|
||||
equal(options.hashIds.baz, 'slave.driver');
|
||||
|
||||
var helpers = {
|
||||
wycats: function(options) {
|
||||
equal(options.hashIds.bat, 'is.a');
|
||||
equal(options.hashIds.baz, 'slave.driver');
|
||||
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.hashIds.bat +
|
||||
':' +
|
||||
options.hash.bat +
|
||||
' ' +
|
||||
options.hashIds.baz +
|
||||
':' +
|
||||
options.hash.baz
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'HELP ME MY BOSS is.a:foo slave.driver:bar'
|
||||
);
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.hashIds.bat +
|
||||
':' +
|
||||
options.hash.bat +
|
||||
' ' +
|
||||
options.hashIds.baz +
|
||||
':' +
|
||||
options.hash.baz
|
||||
);
|
||||
}
|
||||
})
|
||||
.withInput(context)
|
||||
.toCompileTo('HELP ME MY BOSS is.a:foo slave.driver:bar');
|
||||
});
|
||||
|
||||
it('should note ../ and ./ references', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{wycats ./is.a ../slave.driver this.is.a this}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
expectTemplate('{{wycats ./is.a ../slave.driver this.is.a this}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers({
|
||||
wycats: function(passiveVoice, noun, thiz, thiz2, options) {
|
||||
equal(options.ids[0], 'is.a');
|
||||
equal(options.ids[1], '../slave.driver');
|
||||
equal(options.ids[2], 'is.a');
|
||||
equal(options.ids[3], '');
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun, thiz, thiz2, options) {
|
||||
equal(options.ids[0], 'is.a');
|
||||
equal(options.ids[1], '../slave.driver');
|
||||
equal(options.ids[2], 'is.a');
|
||||
equal(options.ids[3], '');
|
||||
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'HELP ME MY BOSS is.a:foo ../slave.driver:undefined'
|
||||
);
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
})
|
||||
.withInput(context)
|
||||
.toCompileTo('HELP ME MY BOSS is.a:foo ../slave.driver:undefined');
|
||||
});
|
||||
|
||||
it('should note @data references', function() {
|
||||
var template = CompilerContext.compile('{{wycats @is.a @slave.driver}}', {
|
||||
trackIds: true
|
||||
});
|
||||
expectTemplate('{{wycats @is.a @slave.driver}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers({
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
equal(options.ids[0], '@is.a');
|
||||
equal(options.ids[1], '@slave.driver');
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
equal(options.ids[0], '@is.a');
|
||||
equal(options.ids[1], '@slave.driver');
|
||||
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(
|
||||
template({}, { helpers: helpers, data: context }),
|
||||
'HELP ME MY BOSS @is.a:foo @slave.driver:bar'
|
||||
);
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
})
|
||||
.withRuntimeOptions({ data: context })
|
||||
.toCompileTo('HELP ME MY BOSS @is.a:foo @slave.driver:bar');
|
||||
});
|
||||
|
||||
it('should return null for constants', function() {
|
||||
var template = CompilerContext.compile('{{wycats 1 "foo" key=false}}', {
|
||||
trackIds: true
|
||||
});
|
||||
expectTemplate('{{wycats 1 "foo" key=false}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers({
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
equal(options.ids[0], null);
|
||||
equal(options.ids[1], null);
|
||||
equal(options.hashIds.key, null);
|
||||
|
||||
var helpers = {
|
||||
wycats: function(passiveVoice, noun, options) {
|
||||
equal(options.ids[0], null);
|
||||
equal(options.ids[1], null);
|
||||
equal(options.hashIds.key, null);
|
||||
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
noun +
|
||||
' ' +
|
||||
options.hash.key
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'HELP ME MY BOSS 1 foo false'
|
||||
);
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
noun +
|
||||
' ' +
|
||||
options.hash.key
|
||||
);
|
||||
}
|
||||
})
|
||||
.withInput(context)
|
||||
.toCompileTo('HELP ME MY BOSS 1 foo false');
|
||||
});
|
||||
|
||||
it('should return true for subexpressions', function() {
|
||||
var template = CompilerContext.compile('{{wycats (sub)}}', {
|
||||
trackIds: true
|
||||
});
|
||||
expectTemplate('{{wycats (sub)}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers({
|
||||
sub: function() {
|
||||
return 1;
|
||||
},
|
||||
wycats: function(passiveVoice, options) {
|
||||
equal(options.ids[0], true);
|
||||
|
||||
var helpers = {
|
||||
sub: function() {
|
||||
return 1;
|
||||
},
|
||||
wycats: function(passiveVoice, options) {
|
||||
equal(options.ids[0], true);
|
||||
|
||||
return 'HELP ME MY BOSS ' + passiveVoice;
|
||||
}
|
||||
};
|
||||
|
||||
equals(template(context, { helpers: helpers }), 'HELP ME MY BOSS 1');
|
||||
return 'HELP ME MY BOSS ' + passiveVoice;
|
||||
}
|
||||
})
|
||||
.withInput(context)
|
||||
.toCompileTo('HELP ME MY BOSS 1');
|
||||
});
|
||||
|
||||
it('should use block param paths', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
expectTemplate('{{#doIt as |is|}}{{wycats is.a slave.driver is}}{{/doIt}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers({
|
||||
doIt: function(options) {
|
||||
var blockParams = [this.is];
|
||||
blockParams.path = ['zomg'];
|
||||
return options.fn(this, { blockParams: blockParams });
|
||||
},
|
||||
wycats: function(passiveVoice, noun, blah, options) {
|
||||
equal(options.ids[0], 'zomg.a');
|
||||
equal(options.ids[1], 'slave.driver');
|
||||
equal(options.ids[2], 'zomg');
|
||||
|
||||
var helpers = {
|
||||
doIt: function(options) {
|
||||
var blockParams = [this.is];
|
||||
blockParams.path = ['zomg'];
|
||||
return options.fn(this, { blockParams: blockParams });
|
||||
},
|
||||
wycats: function(passiveVoice, noun, blah, options) {
|
||||
equal(options.ids[0], 'zomg.a');
|
||||
equal(options.ids[1], 'slave.driver');
|
||||
equal(options.ids[2], 'zomg');
|
||||
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
equals(
|
||||
template(context, { helpers: helpers }),
|
||||
'HELP ME MY BOSS zomg.a:foo slave.driver:bar'
|
||||
);
|
||||
return (
|
||||
'HELP ME MY BOSS ' +
|
||||
options.ids[0] +
|
||||
':' +
|
||||
passiveVoice +
|
||||
' ' +
|
||||
options.ids[1] +
|
||||
':' +
|
||||
noun
|
||||
);
|
||||
}
|
||||
})
|
||||
.withInput(context)
|
||||
.toCompileTo('HELP ME MY BOSS zomg.a:foo slave.driver:bar');
|
||||
});
|
||||
|
||||
describe('builtin helpers', function() {
|
||||
@@ -229,119 +197,85 @@ describe('track ids', function() {
|
||||
|
||||
describe('#each', function() {
|
||||
it('should track contextPath for arrays', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#each array}}{{wycats name}}{{/each}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(
|
||||
template(
|
||||
{ array: [{ name: 'foo' }, { name: 'bar' }] },
|
||||
{ helpers: helpers }
|
||||
),
|
||||
'foo:array.0\nbar:array.1\n'
|
||||
);
|
||||
expectTemplate('{{#each array}}{{wycats name}}{{/each}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput({ array: [{ name: 'foo' }, { name: 'bar' }] })
|
||||
.toCompileTo('foo:array.0\nbar:array.1\n');
|
||||
});
|
||||
|
||||
it('should track contextPath for keys', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#each object}}{{wycats name}}{{/each}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(
|
||||
template(
|
||||
{ object: { foo: { name: 'foo' }, bar: { name: 'bar' } } },
|
||||
{ helpers: helpers }
|
||||
),
|
||||
'foo:object.foo\nbar:object.bar\n'
|
||||
);
|
||||
expectTemplate('{{#each object}}{{wycats name}}{{/each}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput({ object: { foo: { name: 'foo' }, bar: { name: 'bar' } } })
|
||||
.toCompileTo('foo:object.foo\nbar:object.bar\n');
|
||||
});
|
||||
|
||||
it('should handle nesting', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(
|
||||
template(
|
||||
{ array: [{ name: 'foo' }, { name: 'bar' }] },
|
||||
{ helpers: helpers }
|
||||
),
|
||||
'foo:.array..0\nbar:.array..1\n'
|
||||
);
|
||||
expectTemplate(
|
||||
'{{#each .}}{{#each .}}{{wycats name}}{{/each}}{{/each}}'
|
||||
)
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput({ array: [{ name: 'foo' }, { name: 'bar' }] })
|
||||
.toCompileTo('foo:.array..0\nbar:.array..1\n');
|
||||
});
|
||||
it('should handle block params', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#each array as |value|}}{{blockParams value.name}}{{/each}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(
|
||||
template(
|
||||
{ array: [{ name: 'foo' }, { name: 'bar' }] },
|
||||
{ helpers: helpers }
|
||||
),
|
||||
'foo:array.0.name\nbar:array.1.name\n'
|
||||
);
|
||||
it('should handle block params', function() {
|
||||
expectTemplate(
|
||||
'{{#each array as |value|}}{{blockParams value.name}}{{/each}}'
|
||||
)
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput({ array: [{ name: 'foo' }, { name: 'bar' }] })
|
||||
.toCompileTo('foo:array.0.name\nbar:array.1.name\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#with', function() {
|
||||
it('should track contextPath', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#with field}}{{wycats name}}{{/with}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(
|
||||
template({ field: { name: 'foo' } }, { helpers: helpers }),
|
||||
'foo:field\n'
|
||||
);
|
||||
expectTemplate('{{#with field}}{{wycats name}}{{/with}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput({ field: { name: 'foo' } })
|
||||
.toCompileTo('foo:field\n');
|
||||
});
|
||||
it('should handle nesting', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(
|
||||
template({ bat: { field: { name: 'foo' } } }, { helpers: helpers }),
|
||||
'foo:bat.field\n'
|
||||
);
|
||||
it('should handle nesting', function() {
|
||||
expectTemplate(
|
||||
'{{#with bat}}{{#with field}}{{wycats name}}{{/with}}{{/with}}'
|
||||
)
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput({ bat: { field: { name: 'foo' } } })
|
||||
.toCompileTo('foo:bat.field\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#blockHelperMissing', function() {
|
||||
it('should track contextPath for arrays', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#field}}{{wycats name}}{{/field}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(
|
||||
template({ field: [{ name: 'foo' }] }, { helpers: helpers }),
|
||||
'foo:field.0\n'
|
||||
);
|
||||
expectTemplate('{{#field}}{{wycats name}}{{/field}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput({ field: [{ name: 'foo' }] })
|
||||
.toCompileTo('foo:field.0\n');
|
||||
});
|
||||
|
||||
it('should track contextPath for keys', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#field}}{{wycats name}}{{/field}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(
|
||||
template({ field: { name: 'foo' } }, { helpers: helpers }),
|
||||
'foo:field\n'
|
||||
);
|
||||
expectTemplate('{{#field}}{{wycats name}}{{/field}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput({ field: { name: 'foo' } })
|
||||
.toCompileTo('foo:field\n');
|
||||
});
|
||||
it('should handle nesting', function() {
|
||||
var template = CompilerContext.compile(
|
||||
'{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}',
|
||||
{ trackIds: true }
|
||||
);
|
||||
|
||||
equals(
|
||||
template({ bat: { field: { name: 'foo' } } }, { helpers: helpers }),
|
||||
'foo:bat.field\n'
|
||||
);
|
||||
it('should handle nesting', function() {
|
||||
expectTemplate('{{#bat}}{{#field}}{{wycats name}}{{/field}}{{/bat}}')
|
||||
.withCompileOptions({ trackIds: true })
|
||||
.withHelpers(helpers)
|
||||
.withInput({ bat: { field: { name: 'foo' } } })
|
||||
.toCompileTo('foo:bat.field\n');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
}
|
||||
var runner = mocha.run();
|
||||
|
||||
//Reporting for saucelabs
|
||||
// Reporting to test-runner
|
||||
var failedTests = [];
|
||||
runner.on('end', function(){
|
||||
window.mochaResults = runner.stats;
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@
|
||||
}
|
||||
var runner = mocha.run();
|
||||
|
||||
//Reporting for saucelabs
|
||||
// Reporting to test-runner
|
||||
var failedTests = [];
|
||||
runner.on('end', function(){
|
||||
window.mochaResults = runner.stats;
|
||||
|
||||
+3
-5
@@ -15,11 +15,9 @@ describe('utils', function() {
|
||||
it('it should not escape SafeString properties', function() {
|
||||
var name = new Handlebars.SafeString('<em>Sean O'Malley</em>');
|
||||
|
||||
shouldCompileTo(
|
||||
'{{name}}',
|
||||
[{ name: name }],
|
||||
'<em>Sean O'Malley</em>'
|
||||
);
|
||||
expectTemplate('{{name}}')
|
||||
.withInput({ name: name })
|
||||
.toCompileTo('<em>Sean O'Malley</em>');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+118
-87
@@ -2,125 +2,156 @@ describe('whitespace control', function() {
|
||||
it('should strip whitespace around mustache calls', function() {
|
||||
var hash = { foo: 'bar<' };
|
||||
|
||||
shouldCompileTo(' {{~foo~}} ', hash, 'bar<');
|
||||
shouldCompileTo(' {{~foo}} ', hash, 'bar< ');
|
||||
shouldCompileTo(' {{foo~}} ', hash, ' bar<');
|
||||
expectTemplate(' {{~foo~}} ')
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar<');
|
||||
|
||||
shouldCompileTo(' {{~&foo~}} ', hash, 'bar<');
|
||||
shouldCompileTo(' {{~{foo}~}} ', hash, 'bar<');
|
||||
expectTemplate(' {{~foo}} ')
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar< ');
|
||||
|
||||
shouldCompileTo('1\n{{foo~}} \n\n 23\n{{bar}}4', {}, '1\n23\n4');
|
||||
expectTemplate(' {{foo~}} ')
|
||||
.withInput(hash)
|
||||
.toCompileTo(' bar<');
|
||||
|
||||
expectTemplate(' {{~&foo~}} ')
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar<');
|
||||
|
||||
expectTemplate(' {{~{foo}~}} ')
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar<');
|
||||
|
||||
expectTemplate('1\n{{foo~}} \n\n 23\n{{bar}}4').toCompileTo('1\n23\n4');
|
||||
});
|
||||
|
||||
describe('blocks', function() {
|
||||
it('should strip whitespace around simple block calls', function() {
|
||||
var hash = { foo: 'bar<' };
|
||||
|
||||
shouldCompileTo(' {{~#if foo~}} bar {{~/if~}} ', hash, 'bar');
|
||||
shouldCompileTo(' {{#if foo~}} bar {{/if~}} ', hash, ' bar ');
|
||||
shouldCompileTo(' {{~#if foo}} bar {{~/if}} ', hash, ' bar ');
|
||||
shouldCompileTo(' {{#if foo}} bar {{/if}} ', hash, ' bar ');
|
||||
expectTemplate(' {{~#if foo~}} bar {{~/if~}} ')
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar');
|
||||
|
||||
shouldCompileTo(
|
||||
' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ',
|
||||
hash,
|
||||
'bar'
|
||||
);
|
||||
shouldCompileTo(
|
||||
' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ',
|
||||
hash,
|
||||
' abara '
|
||||
);
|
||||
expectTemplate(' {{#if foo~}} bar {{/if~}} ')
|
||||
.withInput(hash)
|
||||
.toCompileTo(' bar ');
|
||||
|
||||
expectTemplate(' {{~#if foo}} bar {{~/if}} ')
|
||||
.withInput(hash)
|
||||
.toCompileTo(' bar ');
|
||||
|
||||
expectTemplate(' {{#if foo}} bar {{/if}} ')
|
||||
.withInput(hash)
|
||||
.toCompileTo(' bar ');
|
||||
|
||||
expectTemplate(' \n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\n ')
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar');
|
||||
|
||||
expectTemplate(' a\n\n{{~#if foo~}} \n\nbar \n\n{{~/if~}}\n\na ')
|
||||
.withInput(hash)
|
||||
.toCompileTo(' abara ');
|
||||
});
|
||||
|
||||
it('should strip whitespace around inverse block calls', function() {
|
||||
var hash = {};
|
||||
expectTemplate(' {{~^if foo~}} bar {{~/if~}} ').toCompileTo('bar');
|
||||
|
||||
shouldCompileTo(' {{~^if foo~}} bar {{~/if~}} ', hash, 'bar');
|
||||
shouldCompileTo(' {{^if foo~}} bar {{/if~}} ', hash, ' bar ');
|
||||
shouldCompileTo(' {{~^if foo}} bar {{~/if}} ', hash, ' bar ');
|
||||
shouldCompileTo(' {{^if foo}} bar {{/if}} ', hash, ' bar ');
|
||||
expectTemplate(' {{^if foo~}} bar {{/if~}} ').toCompileTo(' bar ');
|
||||
|
||||
shouldCompileTo(
|
||||
' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n ',
|
||||
hash,
|
||||
'bar'
|
||||
);
|
||||
expectTemplate(' {{~^if foo}} bar {{~/if}} ').toCompileTo(' bar ');
|
||||
|
||||
expectTemplate(' {{^if foo}} bar {{/if}} ').toCompileTo(' bar ');
|
||||
|
||||
expectTemplate(
|
||||
' \n\n{{~^if foo~}} \n\nbar \n\n{{~/if~}}\n\n '
|
||||
).toCompileTo('bar');
|
||||
});
|
||||
|
||||
it('should strip whitespace around complex block calls', function() {
|
||||
var hash = { foo: 'bar<' };
|
||||
|
||||
shouldCompileTo('{{#if foo~}} bar {{~^~}} baz {{~/if}}', hash, 'bar');
|
||||
shouldCompileTo('{{#if foo~}} bar {{^~}} baz {{/if}}', hash, 'bar ');
|
||||
shouldCompileTo('{{#if foo}} bar {{~^~}} baz {{~/if}}', hash, ' bar');
|
||||
shouldCompileTo('{{#if foo}} bar {{^~}} baz {{/if}}', hash, ' bar ');
|
||||
expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}')
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar');
|
||||
|
||||
shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'bar');
|
||||
expectTemplate('{{#if foo~}} bar {{^~}} baz {{/if}}')
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar ');
|
||||
|
||||
shouldCompileTo(
|
||||
'\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n',
|
||||
hash,
|
||||
'bar'
|
||||
);
|
||||
shouldCompileTo(
|
||||
'\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n',
|
||||
hash,
|
||||
'bar<'
|
||||
);
|
||||
expectTemplate('{{#if foo}} bar {{~^~}} baz {{~/if}}')
|
||||
.withInput(hash)
|
||||
.toCompileTo(' bar');
|
||||
|
||||
hash = {};
|
||||
expectTemplate('{{#if foo}} bar {{^~}} baz {{/if}}')
|
||||
.withInput(hash)
|
||||
.toCompileTo(' bar ');
|
||||
|
||||
shouldCompileTo('{{#if foo~}} bar {{~^~}} baz {{~/if}}', hash, 'baz');
|
||||
shouldCompileTo('{{#if foo}} bar {{~^~}} baz {{/if}}', hash, 'baz ');
|
||||
shouldCompileTo('{{#if foo~}} bar {{~^}} baz {{~/if}}', hash, ' baz');
|
||||
shouldCompileTo('{{#if foo~}} bar {{~^}} baz {{/if}}', hash, ' baz ');
|
||||
expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}')
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar');
|
||||
|
||||
shouldCompileTo('{{#if foo~}} bar {{~else~}} baz {{~/if}}', hash, 'baz');
|
||||
expectTemplate(
|
||||
'\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n'
|
||||
)
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar');
|
||||
|
||||
shouldCompileTo(
|
||||
'\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n',
|
||||
hash,
|
||||
expectTemplate(
|
||||
'\n\n{{~#if foo~}} \n\n{{{foo}}} \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n'
|
||||
)
|
||||
.withInput(hash)
|
||||
.toCompileTo('bar<');
|
||||
|
||||
expectTemplate('{{#if foo~}} bar {{~^~}} baz {{~/if}}').toCompileTo(
|
||||
'baz'
|
||||
);
|
||||
|
||||
expectTemplate('{{#if foo}} bar {{~^~}} baz {{/if}}').toCompileTo('baz ');
|
||||
|
||||
expectTemplate('{{#if foo~}} bar {{~^}} baz {{~/if}}').toCompileTo(
|
||||
' baz'
|
||||
);
|
||||
|
||||
expectTemplate('{{#if foo~}} bar {{~^}} baz {{/if}}').toCompileTo(
|
||||
' baz '
|
||||
);
|
||||
|
||||
expectTemplate('{{#if foo~}} bar {{~else~}} baz {{~/if}}').toCompileTo(
|
||||
'baz'
|
||||
);
|
||||
|
||||
expectTemplate(
|
||||
'\n\n{{~#if foo~}} \n\nbar \n\n{{~^~}} \n\nbaz \n\n{{~/if~}}\n\n'
|
||||
).toCompileTo('baz');
|
||||
});
|
||||
});
|
||||
|
||||
it('should strip whitespace around partials', function() {
|
||||
shouldCompileToWithPartials(
|
||||
'foo {{~> dude~}} ',
|
||||
[{}, {}, { dude: 'bar' }],
|
||||
true,
|
||||
'foobar'
|
||||
);
|
||||
shouldCompileToWithPartials(
|
||||
'foo {{> dude~}} ',
|
||||
[{}, {}, { dude: 'bar' }],
|
||||
true,
|
||||
'foo bar'
|
||||
);
|
||||
shouldCompileToWithPartials(
|
||||
'foo {{> dude}} ',
|
||||
[{}, {}, { dude: 'bar' }],
|
||||
true,
|
||||
'foo bar '
|
||||
);
|
||||
expectTemplate('foo {{~> dude~}} ')
|
||||
.withPartials({ dude: 'bar' })
|
||||
.toCompileTo('foobar');
|
||||
|
||||
shouldCompileToWithPartials(
|
||||
'foo\n {{~> dude}} ',
|
||||
[{}, {}, { dude: 'bar' }],
|
||||
true,
|
||||
'foobar'
|
||||
);
|
||||
shouldCompileToWithPartials(
|
||||
'foo\n {{> dude}} ',
|
||||
[{}, {}, { dude: 'bar' }],
|
||||
true,
|
||||
'foo\n bar'
|
||||
);
|
||||
expectTemplate('foo {{> dude~}} ')
|
||||
.withPartials({ dude: 'bar' })
|
||||
.toCompileTo('foo bar');
|
||||
|
||||
expectTemplate('foo {{> dude}} ')
|
||||
.withPartials({ dude: 'bar' })
|
||||
.toCompileTo('foo bar ');
|
||||
|
||||
expectTemplate('foo\n {{~> dude}} ')
|
||||
.withPartials({ dude: 'bar' })
|
||||
.toCompileTo('foobar');
|
||||
|
||||
expectTemplate('foo\n {{> dude}} ')
|
||||
.withPartials({ dude: 'bar' })
|
||||
.toCompileTo('foo\n bar');
|
||||
});
|
||||
|
||||
it('should only strip whitespace once', function() {
|
||||
var hash = { foo: 'bar' };
|
||||
|
||||
shouldCompileTo(' {{~foo~}} {{foo}} {{foo}} ', hash, 'barbar bar ');
|
||||
expectTemplate(' {{~foo~}} {{foo}} {{foo}} ')
|
||||
.withInput({ foo: 'bar' })
|
||||
.toCompileTo('barbar bar ');
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const metrics = require('../bench');
|
||||
const metrics = require('../tests/bench');
|
||||
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Use `mocha tasks/task-tests` to run these tests
|
||||
+3
-6
@@ -4,7 +4,6 @@ const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
const chai = require('chai');
|
||||
chai.use(require('chai-diff'));
|
||||
const expect = chai.expect;
|
||||
|
||||
const testCases = [
|
||||
@@ -192,11 +191,9 @@ module.exports = function(grunt) {
|
||||
fs.unlinkSync(outputLocation);
|
||||
}
|
||||
|
||||
expect(normalizedOutput).not.to.be.differentFrom(
|
||||
expect(normalizedOutput).to.be.equal(
|
||||
normalizedExpectedOutput,
|
||||
{
|
||||
relaxedSpace: true
|
||||
}
|
||||
`Failed on ${expectedOutputSpec}`
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -226,7 +223,7 @@ function addPathToNodeJs(pathEnvironment) {
|
||||
|
||||
function normalizeCrlf(string) {
|
||||
if (typeof string === 'string') {
|
||||
return string.replace(/\r\n/g, '\n');
|
||||
return string.replace(/\r\n/g, '\n').trim();
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Use `mocha tasks/tests` to run these tests
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['eslint:recommended', 'prettier'],
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 6
|
||||
}
|
||||
};
|
||||
@@ -3,7 +3,7 @@ var _ = require('underscore'),
|
||||
|
||||
module.exports = function(grunt, callback) {
|
||||
// Deferring to here in case we have a build for parser, etc as part of this grunt exec
|
||||
var Handlebars = require('../lib');
|
||||
var Handlebars = require('../../lib');
|
||||
|
||||
var templateSizes = {};
|
||||
_.each(templates, function(info, template) {
|
||||
@@ -145,7 +145,7 @@ function makeSuite(bench, name, template, handlebarsOnly) {
|
||||
|
||||
module.exports = function(grunt, callback) {
|
||||
// Deferring load incase we are being run inline with the grunt build
|
||||
Handlebars = require('../lib');
|
||||
Handlebars = require('../../lib');
|
||||
|
||||
console.log('Execution Throughput');
|
||||
runner(grunt, makeSuite, function(times, scaled) {
|
||||
@@ -11,8 +11,6 @@ function BenchWarmer() {
|
||||
this.errors = {};
|
||||
}
|
||||
|
||||
var print = require('util').print;
|
||||
|
||||
BenchWarmer.prototype = {
|
||||
winners: function(benches) {
|
||||
return Benchmark.filter(benches, 'fastest');
|
||||
@@ -69,7 +67,7 @@ BenchWarmer.prototype = {
|
||||
|
||||
self.startLine('');
|
||||
|
||||
print('\n');
|
||||
console.log('\n');
|
||||
self.printHeader('scaled');
|
||||
_.each(self.scaled, function(value, name) {
|
||||
self.startLine(name);
|
||||
@@ -78,7 +76,7 @@ BenchWarmer.prototype = {
|
||||
self.writeValue(value[lang] || '');
|
||||
});
|
||||
});
|
||||
print('\n');
|
||||
console.log('\n');
|
||||
|
||||
var errors = false,
|
||||
prop,
|
||||
@@ -94,16 +92,16 @@ BenchWarmer.prototype = {
|
||||
}
|
||||
|
||||
if (errors) {
|
||||
print('\n\nErrors:\n');
|
||||
console.log('\n\nErrors:\n');
|
||||
Object.keys(self.errors).forEach(function(prop) {
|
||||
if (self.errors[prop].error.message !== 'EWOT') {
|
||||
bench = self.errors[prop];
|
||||
print('\n' + bench.name + ':\n');
|
||||
print(bench.error.message);
|
||||
console.log('\n' + bench.name + ':\n');
|
||||
console.log(bench.error.message);
|
||||
if (bench.error.stack) {
|
||||
print(bench.error.stack.join('\n'));
|
||||
console.log(bench.error.stack.join('\n'));
|
||||
}
|
||||
print('\n');
|
||||
console.log('\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -112,7 +110,7 @@ BenchWarmer.prototype = {
|
||||
}
|
||||
});
|
||||
|
||||
print('\n');
|
||||
console.log('\n');
|
||||
},
|
||||
|
||||
scaleTimes: function() {
|
||||
@@ -163,11 +161,11 @@ BenchWarmer.prototype = {
|
||||
}
|
||||
|
||||
if (winners) {
|
||||
print('WINNER(S)');
|
||||
console.log('WINNER(S)');
|
||||
horSize = horSize + 'WINNER(S)'.length;
|
||||
}
|
||||
|
||||
print('\n' + new Array(horSize + 1).join('-'));
|
||||
console.log('\n' + new Array(horSize + 1).join('-'));
|
||||
},
|
||||
|
||||
startLine: function(name) {
|
||||
@@ -179,8 +177,8 @@ BenchWarmer.prototype = {
|
||||
|
||||
this.currentBenches = [];
|
||||
|
||||
print(winners.join(', '));
|
||||
print('\n');
|
||||
console.log(winners.join(', '));
|
||||
console.log('\n');
|
||||
|
||||
if (name) {
|
||||
this.writeValue(name);
|
||||
@@ -216,7 +214,7 @@ BenchWarmer.prototype = {
|
||||
writeValue: function(out) {
|
||||
var padding = this.benchSize - out.length + 1;
|
||||
out = out + new Array(padding).join(' ');
|
||||
print(out);
|
||||
console.log(out);
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user