Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93b0c2d892 | |||
| d262378b7c | |||
| 7729c84ec7 | |||
| dffeef29d7 | |||
| 2e22588ccf | |||
| 0b0acb0342 | |||
| 18fc43e828 | |||
| ed47d811e2 | |||
| a84f9f6178 | |||
| 4fbbe1152e | |||
| df3d941c57 | |||
| 2bd3214a55 | |||
| fc2abef327 | |||
| 76a0eb89fb | |||
| ee57b4c26b | |||
| 29eaabc000 | |||
| 2a6081057f | |||
| 79538afd7b | |||
| bcfa64e77c | |||
| b7a2deed30 | |||
| 98ff901bda | |||
| 428b81cba9 | |||
| 5dae9c230e | |||
| c3fad1157e | |||
| 9242c580a1 | |||
| c2860944c6 | |||
| e9a00fbdc3 | |||
| d018ac2a9a | |||
| b770c353bc | |||
| 812277c257 | |||
| 61871da9de | |||
| 9ee075518f | |||
| afe93eaff8 | |||
| d8f94d1a3f | |||
| f53b53df22 | |||
| eab5731afc | |||
| 986c446aaf | |||
| 60366c8d0b | |||
| 494c8aa0b3 | |||
| cd9459e129 | |||
| 2862883bd8 | |||
| bbb673a48a |
+1
-1
@@ -24,7 +24,7 @@ install:
|
||||
- npm config set spin false
|
||||
# Log HTTP requests
|
||||
- npm config set loglevel http
|
||||
- time ./scripts/travis/fetch_bundle.sh
|
||||
- time ./scripts/travis/npm-bundle-deps.sh
|
||||
- time npm install
|
||||
|
||||
before_script:
|
||||
|
||||
+152
@@ -1,3 +1,125 @@
|
||||
<a name="1.3.0-beta.17"></a>
|
||||
# 1.3.0-beta.17 turing-autocompletion (2014-07-25)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **angular.copy:** clone regexp flags correctly
|
||||
([86340a59](https://github.com/angular/angular.js/commit/86340a59bf9eb7bdfc4f99000cecf628cd10d9c8),
|
||||
[#5781](https://github.com/angular/angular.js/issues/5781), [#8337](https://github.com/angular/angular.js/issues/8337))
|
||||
- **docs:** change plnkr form to open in same window
|
||||
([925b2080](https://github.com/angular/angular.js/commit/925b2080a0341d9348feeb4f492957a2e2c80082))
|
||||
- **jqLite:** triggerHandler support unbind self
|
||||
([8a27abae](https://github.com/angular/angular.js/commit/8a27abae896de3c4d94c407e8bb381e099d2d7f7),
|
||||
[#5984](https://github.com/angular/angular.js/issues/5984))
|
||||
- **ngHref:** remove attribute when empty value instead of ignoring
|
||||
([469ea338](https://github.com/angular/angular.js/commit/469ea3384ad48ca4765af807c0f41201edb527f9),
|
||||
[#2755](https://github.com/angular/angular.js/issues/2755))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** change directive's restrict setting to default to EA (element/attribute)
|
||||
([11f5aeee](https://github.com/angular/angular.js/commit/11f5aeeee952a395edaf54e3277674f211a82fc7),
|
||||
[#8321](https://github.com/angular/angular.js/issues/8321))
|
||||
- **$q:** add streamlined ES6-style interface for using $q
|
||||
([f3a763fd](https://github.com/angular/angular.js/commit/f3a763fd2edd8a37b80c79a5aaa1444460cd2df7),
|
||||
[#8311](https://github.com/angular/angular.js/issues/8311), [#6427](https://github.com/angular/angular.js/issues/6427))
|
||||
- **ngRepeat:** provide support for aliasing filtered repeater results as a scope member
|
||||
([e0adb9c4](https://github.com/angular/angular.js/commit/e0adb9c452e172295209f785b62472688225fffb),
|
||||
[#5919](https://github.com/angular/angular.js/issues/5919), [#8046](https://github.com/angular/angular.js/issues/8046), [#8282](https://github.com/angular/angular.js/issues/8282))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$parse:** don't use reflective calls in generated functions
|
||||
([c54228fb](https://github.com/angular/angular.js/commit/c54228fbe9d42d8a3a159bf84dd1d2e99b259ece))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$compile:** due to [11f5aeee](https://github.com/angular/angular.js/commit/11f5aeeee952a395edaf54e3277674f211a82fc7),
|
||||
directives now match elements by default unless specific restriction rules are set via `restrict` property.
|
||||
|
||||
This means that if a directive 'myFoo' previously didn't specify matching restrictrion, it will now match both the attribute
|
||||
and element form.
|
||||
|
||||
Before:
|
||||
|
||||
`<div my-foo></div>` <---- my-foo attribute matched the directive
|
||||
|
||||
`<my-foo></my-foo>` <---- no match
|
||||
|
||||
After:
|
||||
|
||||
`<div my-foo></div>` <---- my-foo attribute matched the directive
|
||||
|
||||
`<my-foo></my-foo>` <---- my-foo element matched the directive
|
||||
|
||||
It is not expected that this will be a problem in practice because of widespread use of prefixes that make `<my-foo>` like
|
||||
elements unlikely.
|
||||
|
||||
Closes #8321
|
||||
|
||||
|
||||
<a name="1.2.21"></a>
|
||||
# 1.2.21 wizard-props (2014-07-25)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$http:** fix double-quoted date issue when encoding params
|
||||
([2f960f15](https://github.com/angular/angular.js/commit/2f960f1530ed936c57df612a352a0d996368f6a1),
|
||||
[#8150](https://github.com/angular/angular.js/issues/8150), [#6128](https://github.com/angular/angular.js/issues/6128), [#8154](https://github.com/angular/angular.js/issues/8154))
|
||||
- **$location:** handle plus character in query strings
|
||||
([60af504c](https://github.com/angular/angular.js/commit/60af504c18dbdde9dfe90e9a2badef6d9e798512),
|
||||
[#3042](https://github.com/angular/angular.js/issues/3042))
|
||||
- **$rootScope:** $watchCollection should handle NaN in objects
|
||||
([bf13d268](https://github.com/angular/angular.js/commit/bf13d2683d5880b18db00087e80ee0fd5e1f429a),
|
||||
[#7930](https://github.com/angular/angular.js/issues/7930))
|
||||
- **angular.copy:** clone regexp flags correctly
|
||||
([e25ed0d4](https://github.com/angular/angular.js/commit/e25ed0d48d9a1c577e78b1c96098841572c764ea),
|
||||
[#5781](https://github.com/angular/angular.js/issues/5781), [#8337](https://github.com/angular/angular.js/issues/8337))
|
||||
- **csp:** fix autodetection of CSP + better docs
|
||||
([0e5d3190](https://github.com/angular/angular.js/commit/0e5d31908e122f013427164f7bbeea914a9a5961),
|
||||
[#8162](https://github.com/angular/angular.js/issues/8162), [#8191](https://github.com/angular/angular.js/issues/8191))
|
||||
- **docs:** change plnkr form to open in same window
|
||||
([5d11e020](https://github.com/angular/angular.js/commit/5d11e02008731a78f302841863a83fe7ed3c37b9))
|
||||
- **jqLite:** triggerHandler support unbind self
|
||||
([209e6000](https://github.com/angular/angular.js/commit/209e60007042f7e8b34c54ec6bf7d6f703c0ba2a),
|
||||
[#5984](https://github.com/angular/angular.js/issues/5984))
|
||||
- **ngHref:** remove attribute when empty value instead of ignoring
|
||||
([948c86c6](https://github.com/angular/angular.js/commit/948c86c6025fca8e07921869d21cfac1c6333b05),
|
||||
[#2755](https://github.com/angular/angular.js/issues/2755))
|
||||
- **ngRoute:** remove unnecessary call to decodeURIComponent
|
||||
([1b779028](https://github.com/angular/angular.js/commit/1b779028fdd339febaa1fff5f3bd4cfcda46cc09),
|
||||
[#6326](https://github.com/angular/angular.js/issues/6326), [#6327](https://github.com/angular/angular.js/issues/6327))
|
||||
- **ngSanitize:**
|
||||
- follow HTML parser rules for start tags / allow < in text content
|
||||
([d175bb01](https://github.com/angular/angular.js/commit/d175bb01314efdcbad5c3cb31b02e298e26c6e19),
|
||||
[#8212](https://github.com/angular/angular.js/issues/8212), [#8193](https://github.com/angular/angular.js/issues/8193))
|
||||
- **orderBy:** correctly order by date values
|
||||
([f1b28847](https://github.com/angular/angular.js/commit/f1b28847c8123483e03ac2410de86fd33a80b5f4),
|
||||
[#6675](https://github.com/angular/angular.js/issues/6675), [#6746](https://github.com/angular/angular.js/issues/6746))
|
||||
- **select:** force visual update in IE
|
||||
([c0afbfac](https://github.com/angular/angular.js/commit/c0afbfaca57893403d8d4b0990879ad5b9ffc3e5),
|
||||
[#7692](https://github.com/angular/angular.js/issues/7692), [#8158](https://github.com/angular/angular.js/issues/8158))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$compile:** only create jqLite object when necessary
|
||||
([71eb1901](https://github.com/angular/angular.js/commit/71eb1901f6b9a3a6d4b772aa95ce0dc78ff847bc))
|
||||
- **$parse:** don't use reflective calls in generated functions
|
||||
([cbdf0c2a](https://github.com/angular/angular.js/commit/cbdf0c2afb9836ae4cca6d70cf555ff28f55a1d1))
|
||||
- **forEach:** use native for loop instead of forEach for Arrays
|
||||
([492b0cdf](https://github.com/angular/angular.js/commit/492b0cdf28d02f1d508455245b7d8e1d641d9f40))
|
||||
- **jqLite:** expose the low-level jqLite.data/removeData calls
|
||||
([3c46c943](https://github.com/angular/angular.js/commit/3c46c94342aa35131f3ba0f8f4a6b39338b87d56))
|
||||
- **ngBindHtml:** move addClass to the compile phase
|
||||
([8eede099](https://github.com/angular/angular.js/commit/8eede099cd8aa6d524d1de385d08432072fd294e),
|
||||
[#8261](https://github.com/angular/angular.js/issues/8261))
|
||||
|
||||
|
||||
<a name="1.3.0-beta.16"></a>
|
||||
# 1.3.0-beta.16 pizza-transubstantiation (2014-07-18)
|
||||
@@ -500,6 +622,36 @@ Closes #3969
|
||||
Closes #4277
|
||||
Closes #7960
|
||||
|
||||
- **$timeout/$interval:**
|
||||
- due to [19b6b343](https://github.com/angular/angular.js/commit/19b6b3433ae9f8523cbc72ae97dbcf0c06960148)
|
||||
|
||||
|
||||
Previously, even if invokeApply was set to false, a $rootScope digest would occur during promise
|
||||
resolution. This is no longer the case, as promises returned from $timeout and $interval will no
|
||||
longer trigger $evalAsync (which in turn causes a $digest) if `invokeApply` is false.
|
||||
|
||||
Workarounds include manually triggering $scope.$apply(), or returning $q.defer().promise from a
|
||||
promise callback, and resolving or rejecting it when appropriate.
|
||||
|
||||
var interval = $interval(function() {
|
||||
if (someRequirementFulfilled) {
|
||||
$interval.cancel(interval);
|
||||
$scope.$apply();
|
||||
}
|
||||
}, 100, 0, false);
|
||||
|
||||
or:
|
||||
|
||||
var interval = $interval(function (idx) {
|
||||
// make the magic happen
|
||||
}, 1000, 10, false);
|
||||
interval.then(function(idx) {
|
||||
var deferred = $q.defer();
|
||||
// do the asynchronous magic --- $evalAsync will cause a digest and cause
|
||||
// bindings to update.
|
||||
return deferred.promise;
|
||||
});
|
||||
|
||||
<a name="1.2.19"></a>
|
||||
# 1.2.19 precognitive-flashbacks (2014-06-30)
|
||||
|
||||
|
||||
@@ -25,3 +25,33 @@ angular.module('myApp', [])
|
||||
// Do something with myService
|
||||
}]);
|
||||
```
|
||||
|
||||
An unknown provider error can also be caused by accidentally redefining a
|
||||
module using the `angular.module` API, as shown in the following example.
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.service('myCoolService', function () { /* ... */ });
|
||||
|
||||
angular.module('myModule', [])
|
||||
// myModule has already been created! This is not what you want!
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition throws unknown provider, because myCoolService
|
||||
// has been destroyed.
|
||||
}]);
|
||||
```
|
||||
|
||||
To fix this problem, make sure you only define each module with the
|
||||
`angular.module(name, [requires])` syntax once across your entire project.
|
||||
Retrieve it for subsequent use with `angular.module(name)`. The fixed example
|
||||
is shown below.
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.service('myCoolService', function () { /* ... */ });
|
||||
|
||||
angular.module('myModule')
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition does not throw unknown provider.
|
||||
}]);
|
||||
```
|
||||
@@ -909,6 +909,30 @@ Looking back at `myPane`'s definition, notice the last argument in its `link` fu
|
||||
When a directive requires a controller, it receives that controller as the fourth argument of its
|
||||
`link` function. Taking advantage of this, `myPane` can call the `addPane` function of `myTabs`.
|
||||
|
||||
If multiple controllers are required, the `require` option of the directive can take an array argument.
|
||||
The corresponding parameter being sent to the `link` function will also be an array.
|
||||
|
||||
```js
|
||||
angular.module('docsTabsExample', [])
|
||||
.directive('myPane', function() {
|
||||
return {
|
||||
require: ['^myTabs', '^ngModel'],
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {
|
||||
title: '@'
|
||||
},
|
||||
link: function(scope, element, attrs, controllers) {
|
||||
var tabsCtrl = controllers[0],
|
||||
modelCtrl = controllers[1];
|
||||
|
||||
tabsCtrl.addPane(scope);
|
||||
},
|
||||
templateUrl: 'my-pane.html'
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
Savvy readers may be wondering what the difference is between `link` and `controller`.
|
||||
The basic difference is that `controller` can expose an API, and `link` functions can interact with
|
||||
controllers using `require`.
|
||||
|
||||
@@ -47,7 +47,7 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||
|
||||
### Testing
|
||||
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/dev_guide.unit-testing Unit testing}, {@link guide/dev_guide.services.testing_services Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/unit-testing Unit testing}, {@link guide/services#unit-testing Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
|
||||
|
||||
## Specific Topics
|
||||
|
||||
@@ -382,8 +382,6 @@ See [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a
|
||||
|
||||
## ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||||
|
||||
`ngBindHtml` which has been moved from `ngSanitize` module to the core `ng` module.
|
||||
|
||||
`ngBindHtml` provides `ngBindHtmlUnsafe` like
|
||||
behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result
|
||||
of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanitized via
|
||||
@@ -391,6 +389,10 @@ of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanit
|
||||
module is not loaded) and the bound expression evaluates to a value that is not trusted an
|
||||
exception is thrown.
|
||||
|
||||
When using this directive you can either include `ngSanitize` in your module's dependencis (See the
|
||||
example at the {@link ngBindHtml} reference) or use the {@link $sce} service to set the value as
|
||||
trusted.
|
||||
|
||||
See [dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55).
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ tag as the template.
|
||||
by the value of the expressions.
|
||||
|
||||
We have added a new directive, called `ng-controller`, which attaches a `PhoneListCtrl`
|
||||
__controller__ to the DOM at this point:
|
||||
__controller__ to the <body> tag. At this point:
|
||||
|
||||
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}` denote
|
||||
bindings, which are referring to our application model, which is set up in our `PhoneListCtrl`
|
||||
@@ -210,6 +210,11 @@ To run the tests, and then watch the files for changes: `npm test`.
|
||||
* To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
<div class="alert alert-info">
|
||||
Make sure you don't minimize the browser that Karma opened. On some OS, memory assigned to a minimized
|
||||
browser is limited, which results in your karma tests running extremely slow.
|
||||
</div>
|
||||
|
||||
# Experiments
|
||||
|
||||
* Add another binding to `index.html`. For example:
|
||||
|
||||
@@ -145,16 +145,39 @@ Display the current value of the `query` model by adding a `{{query}}` binding i
|
||||
### Display Query in Title
|
||||
Let's see how we can get the current value of the `query` model to appear in the HTML page title.
|
||||
|
||||
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
|
||||
* Add an end-to-end test into the `describe` block, `test/e2e/scenarios.js` should look like this:
|
||||
|
||||
```js
|
||||
it('should display the current filter value in the title bar', function() {
|
||||
describe('PhoneCat App', function() {
|
||||
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
|
||||
|
||||
element(by.model('query')).sendKeys('nexus');
|
||||
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
|
||||
describe('Phone list view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('app/index.html');
|
||||
});
|
||||
|
||||
var phoneList = element.all(by.repeater('phone in phones'));
|
||||
var query = element(by.model('query'));
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
expect(phoneList.count()).toBe(3);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
expect(phoneList.count()).toBe(1);
|
||||
|
||||
query.clear();
|
||||
query.sendKeys('motorola');
|
||||
expect(phoneList.count()).toBe(2);
|
||||
});
|
||||
|
||||
it('should display the current filter value in the title bar', function() {
|
||||
query.clear();
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ moved the controllers into their own module `phonecatControllers` (as shown belo
|
||||
|
||||
We added `angular-route.js` to `index.html` and created a new `phonecatControllers` module in
|
||||
`controllers.js`. That's not all we need to do to be able to use their code, however. We also have
|
||||
to add the modules dependencies of our app. By listing these two modules as dependencies of
|
||||
to add the modules as dependencies of our app. By listing these two modules as dependencies of
|
||||
`phonecatApp`, we can use the directives and services they provide.
|
||||
|
||||
|
||||
|
||||
@@ -102,6 +102,50 @@ __`test/e2e/scenarios.js`:__
|
||||
You can now rerun `npm run protractor` to see the tests run.
|
||||
|
||||
|
||||
You also have to refactor one of your unit tests because of the addition of the `mainImageUrl`
|
||||
model property to the `PhoneDetailCtrl` controller. Below, we create the function `xyzPhoneData`
|
||||
which returns the appropriate json with the `images` attribute in order to get the test to pass.
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
|
||||
```js
|
||||
...
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
...
|
||||
|
||||
describe('PhoneDetailCtrl', function(){
|
||||
var scope, $httpBackend, ctrl,
|
||||
xyzPhoneData = function() {
|
||||
return {
|
||||
name: 'phone xyz',
|
||||
images: ['image/url1.png', 'image/url2.png']
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $routeParams, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData());
|
||||
|
||||
$routeParams.phoneId = 'xyz';
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller('PhoneDetailCtrl', {$scope: scope});
|
||||
}));
|
||||
|
||||
|
||||
it('should fetch phone detail', function() {
|
||||
expect(scope.phone).toBeUndefined();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(scope.phone).toEqual(xyzPhoneData());
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Your unit tests should now be passing.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
* Let's add a new controller method to `PhoneDetailCtrl`:
|
||||
|
||||
@@ -12,9 +12,9 @@ set -e
|
||||
# before_script:
|
||||
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
|
||||
|
||||
CONNECT_URL="http://saucelabs.com/downloads/Sauce-Connect-latest.zip"
|
||||
CONNECT_URL="https://d2nkw87yt5k0to.cloudfront.net/downloads/sc-4.3-linux.tar.gz"
|
||||
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
|
||||
CONNECT_DOWNLOAD="Sauce_Connect.zip"
|
||||
CONNECT_DOWNLOAD="sc-4.3-linux.tar.gz"
|
||||
|
||||
CONNECT_LOG="$LOGS_DIR/sauce-connect"
|
||||
CONNECT_STDOUT="$LOGS_DIR/sauce-connect.stdout"
|
||||
@@ -24,7 +24,8 @@ CONNECT_STDERR="$LOGS_DIR/sauce-connect.stderr"
|
||||
mkdir -p $CONNECT_DIR
|
||||
cd $CONNECT_DIR
|
||||
curl $CONNECT_URL -o $CONNECT_DOWNLOAD 2> /dev/null 1> /dev/null
|
||||
unzip $CONNECT_DOWNLOAD > /dev/null
|
||||
mkdir sauce-connect
|
||||
tar --extract --file=$CONNECT_DOWNLOAD --strip-components=1 --directory=sauce-connect > /dev/null
|
||||
rm $CONNECT_DOWNLOAD
|
||||
|
||||
SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
@@ -45,5 +46,5 @@ echo "Starting Sauce Connect in the background, logging into:"
|
||||
echo " $CONNECT_LOG"
|
||||
echo " $CONNECT_STDOUT"
|
||||
echo " $CONNECT_STDERR"
|
||||
java -jar Sauce-Connect.jar $ARGS $SAUCE_USERNAME $SAUCE_ACCESS_KEY \
|
||||
sauce-connect/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY $ARGS -v \
|
||||
--logfile $CONNECT_LOG 2> $CONNECT_STDERR 1> $CONNECT_STDOUT &
|
||||
|
||||
Generated
+1
-1
@@ -502,7 +502,7 @@
|
||||
}
|
||||
},
|
||||
"dgeni-packages": {
|
||||
"version": "0.9.6",
|
||||
"version": "0.9.7",
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "2.4.1"
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@
|
||||
"canonical-path": "0.0.2",
|
||||
"winston": "~0.7.2",
|
||||
"dgeni": "^0.3.0",
|
||||
"dgeni-packages": "^0.9.6",
|
||||
"dgeni-packages": "^0.9.7",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"jshint-stylish": "~0.1.5",
|
||||
"node-html-encoder": "0.0.2",
|
||||
|
||||
@@ -7,9 +7,11 @@ config.sauceKey = process.env.SAUCE_ACCESS_KEY;
|
||||
|
||||
config.multiCapabilities = [{
|
||||
'browserName': 'chrome',
|
||||
'platform': 'OS X 10.9',
|
||||
'name': 'Angular E2E',
|
||||
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
|
||||
'build': process.env.TRAVIS_BUILD_NUMBER
|
||||
'build': process.env.TRAVIS_BUILD_NUMBER,
|
||||
'version': '34'
|
||||
}, {
|
||||
'browserName': 'firefox',
|
||||
'name': 'Angular E2E',
|
||||
|
||||
@@ -6,4 +6,4 @@ set -e
|
||||
cd $(dirname $0);
|
||||
|
||||
cd ../..
|
||||
curl "http://23.251.148.50:8000/tar/$TRAVIS_REPO_SLUG/$TRAVIS_COMMIT" | tar xz
|
||||
curl "http://23.251.148.50:8000/tar/$TRAVIS_REPO_SLUG/$TRAVIS_COMMIT" | tar xz || true
|
||||
@@ -109,11 +109,11 @@ function publishExternalAPI(angular){
|
||||
'element': jqLite,
|
||||
'forEach': forEach,
|
||||
'injector': createInjector,
|
||||
'noop':noop,
|
||||
'bind':bind,
|
||||
'noop': noop,
|
||||
'bind': bind,
|
||||
'toJson': toJson,
|
||||
'fromJson': fromJson,
|
||||
'identity':identity,
|
||||
'identity': identity,
|
||||
'isUndefined': isUndefined,
|
||||
'isDefined': isDefined,
|
||||
'isString': isString,
|
||||
|
||||
+28
-12
@@ -952,21 +952,37 @@ forEach({
|
||||
|
||||
clone: jqLiteClone,
|
||||
|
||||
triggerHandler: function(element, eventName, eventData) {
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName],
|
||||
eventFnsCopy = shallowCopy(eventFns || []);
|
||||
triggerHandler: function(element, event, extraParameters) {
|
||||
|
||||
eventData = eventData || [];
|
||||
var dummyEvent, eventFnsCopy, handlerArgs;
|
||||
var eventName = event.type || event;
|
||||
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
|
||||
|
||||
var event = [{
|
||||
preventDefault: noop,
|
||||
stopPropagation: noop
|
||||
}];
|
||||
if (eventFns) {
|
||||
|
||||
forEach(eventFnsCopy, function(fn) {
|
||||
fn.apply(element, event.concat(eventData));
|
||||
});
|
||||
// Create a dummy event to pass to the handlers
|
||||
dummyEvent = {
|
||||
preventDefault: function() { this.defaultPrevented = true; },
|
||||
isDefaultPrevented: function() { return this.defaultPrevented === true; },
|
||||
stopPropagation: noop,
|
||||
type: eventName,
|
||||
target: element
|
||||
};
|
||||
|
||||
// If a custom event was provided then extend our dummy event with it
|
||||
if (event.type) {
|
||||
dummyEvent = extend(dummyEvent, event);
|
||||
}
|
||||
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
eventFnsCopy = shallowCopy(eventFns);
|
||||
handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
|
||||
|
||||
forEach(eventFnsCopy, function(fn) {
|
||||
fn.apply(element, handlerArgs);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}, function(fn, name){
|
||||
/**
|
||||
|
||||
+2
-2
@@ -214,7 +214,7 @@
|
||||
*
|
||||
* #### `template`
|
||||
* HTML markup that may:
|
||||
* * Replace the contents of the directive's element (defualt).
|
||||
* * Replace the contents of the directive's element (default).
|
||||
* * Replace the directive's element itself (if `replace` is true - DEPRECATED).
|
||||
* * Wrap the contents of the directive's element (if `transclude` is true).
|
||||
*
|
||||
@@ -1474,7 +1474,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (parentGet.literal) {
|
||||
compare = equals;
|
||||
} else {
|
||||
compare = function(a,b) { return a === b; };
|
||||
compare = function(a,b) { return a === b || (a !== a && b !== b); };
|
||||
}
|
||||
parentSet = parentGet.assign || function() {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
|
||||
@@ -307,6 +307,13 @@ forEach(
|
||||
* server and reloading the current page), but only if the form does not contain `action`,
|
||||
* `data-action`, or `x-action` attributes.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
|
||||
* `ngSubmit` handlers together. See the
|
||||
* {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
|
||||
* for a detailed discussion of when `ngSubmit` may be triggered.
|
||||
* </div>
|
||||
*
|
||||
* @element form
|
||||
* @priority 0
|
||||
* @param {expression} ngSubmit {@link guide/expression Expression} to eval.
|
||||
|
||||
+27
-21
@@ -395,21 +395,37 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
value = valueFn(scope, locals);
|
||||
}
|
||||
}
|
||||
// Update the null option's selected property here so $render cleans it up correctly
|
||||
if (optionGroupsCache[0].length > 1) {
|
||||
if (optionGroupsCache[0][1].id !== key) {
|
||||
optionGroupsCache[0][1].selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctrl.$setViewValue(value);
|
||||
render();
|
||||
});
|
||||
});
|
||||
|
||||
ctrl.$render = render;
|
||||
|
||||
// TODO(vojta): can't we optimize this ?
|
||||
scope.$watch(render);
|
||||
scope.$watchCollection(valuesFn, render);
|
||||
if ( multiple ) {
|
||||
scope.$watchCollection(function() { return ctrl.$modelValue; }, render);
|
||||
}
|
||||
|
||||
function getSelectedSet() {
|
||||
var selectedSet = false;
|
||||
if (multiple) {
|
||||
var modelValue = ctrl.$modelValue;
|
||||
if (trackFn && isArray(modelValue)) {
|
||||
selectedSet = new HashMap([]);
|
||||
var locals = {};
|
||||
for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
|
||||
locals[valueName] = modelValue[trackIndex];
|
||||
selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
|
||||
}
|
||||
} else {
|
||||
selectedSet = new HashMap(modelValue);
|
||||
}
|
||||
}
|
||||
return selectedSet;
|
||||
}
|
||||
|
||||
|
||||
function render() {
|
||||
// Temporary location for the option groups before we render them
|
||||
@@ -427,22 +443,11 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
groupIndex, index,
|
||||
locals = {},
|
||||
selected,
|
||||
selectedSet = false, // nothing is selected yet
|
||||
selectedSet = getSelectedSet(),
|
||||
lastElement,
|
||||
element,
|
||||
label;
|
||||
|
||||
if (multiple) {
|
||||
if (trackFn && isArray(modelValue)) {
|
||||
selectedSet = new HashMap([]);
|
||||
for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
|
||||
locals[valueName] = modelValue[trackIndex];
|
||||
selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
|
||||
}
|
||||
} else {
|
||||
selectedSet = new HashMap(modelValue);
|
||||
}
|
||||
}
|
||||
|
||||
// We now build up the list of options we need (we merge later)
|
||||
for (index = 0; length = keys.length, index < length; index++) {
|
||||
@@ -538,7 +543,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
lastElement.val(existingOption.id = option.id);
|
||||
}
|
||||
// lastElement.prop('selected') provided by jQuery has side-effects
|
||||
if (existingOption.selected !== option.selected) {
|
||||
if (lastElement[0].selected !== option.selected) {
|
||||
lastElement.prop('selected', (existingOption.selected = option.selected));
|
||||
if (msie) {
|
||||
// See #7692
|
||||
@@ -561,6 +566,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
(element = optionTemplate.clone())
|
||||
.val(option.id)
|
||||
.prop('selected', option.selected)
|
||||
.attr('selected', option.selected)
|
||||
.text(option.label);
|
||||
}
|
||||
|
||||
|
||||
@@ -327,7 +327,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
* (e.g. `"h 'o''clock'"`).
|
||||
*
|
||||
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
|
||||
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
|
||||
* specified in the string input, the time is considered to be in the local timezone.
|
||||
* @param {string=} format Formatting rules (see Description). If not specified,
|
||||
|
||||
+4
-2
@@ -275,6 +275,7 @@ function $HttpProvider() {
|
||||
* - {@link ng.$http#put $http.put}
|
||||
* - {@link ng.$http#delete $http.delete}
|
||||
* - {@link ng.$http#jsonp $http.jsonp}
|
||||
* - {@link ng.$http#patch $http.patch}
|
||||
*
|
||||
*
|
||||
* # Setting HTTP Headers
|
||||
@@ -576,7 +577,7 @@ function $HttpProvider() {
|
||||
* - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
|
||||
* that should abort the request when resolved.
|
||||
* - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
|
||||
* XHR object. See [requests with credentials]https://developer.mozilla.org/en/http_access_control#section_5
|
||||
* XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
|
||||
* for more information.
|
||||
* - **responseType** - `{string}` - see
|
||||
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
|
||||
@@ -945,7 +946,8 @@ function $HttpProvider() {
|
||||
promise.then(removePendingReq, removePendingReq);
|
||||
|
||||
|
||||
if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
|
||||
if ((config.cache || defaults.cache) && config.cache !== false &&
|
||||
(config.method === 'GET' || config.method === 'JSONP')) {
|
||||
cache = isObject(config.cache) ? config.cache
|
||||
: isObject(defaults.cache) ? defaults.cache
|
||||
: defaultCache;
|
||||
|
||||
@@ -635,6 +635,8 @@ function $LocationProvider(){
|
||||
$location = new LocationMode(appBase, '#' + hashPrefix);
|
||||
$location.$$parse($location.$$rewrite(initialUrl));
|
||||
|
||||
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
|
||||
|
||||
$rootElement.on('click', function(event) {
|
||||
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
|
||||
// currently we open nice url link and redirect then
|
||||
@@ -657,6 +659,9 @@ function $LocationProvider(){
|
||||
absHref = urlResolve(absHref.animVal).href;
|
||||
}
|
||||
|
||||
// Ignore when url is started with javascript: or mailto:
|
||||
if (IGNORE_URI_REGEXP.test(absHref)) return;
|
||||
|
||||
// Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9)
|
||||
// The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or
|
||||
// somewhere#anchor or http://example.com/somewhere
|
||||
|
||||
+9
-6
@@ -606,9 +606,9 @@ Parser.prototype = {
|
||||
var middle;
|
||||
var token;
|
||||
if ((token = this.expect('?'))) {
|
||||
middle = this.ternary();
|
||||
middle = this.assignment();
|
||||
if ((token = this.expect(':'))) {
|
||||
return this.ternaryFn(left, middle, this.ternary());
|
||||
return this.ternaryFn(left, middle, this.assignment());
|
||||
} else {
|
||||
this.throwError('expected :', token);
|
||||
}
|
||||
@@ -696,7 +696,9 @@ Parser.prototype = {
|
||||
return getter(self || object(scope, locals));
|
||||
}, {
|
||||
assign: function(scope, value, locals) {
|
||||
return setter(object(scope, locals), field, value, parser.text, parser.options);
|
||||
var o = object(scope, locals);
|
||||
if (!o) object.assign(scope, o = {});
|
||||
return setter(o, field, value, parser.text, parser.options);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -726,10 +728,11 @@ Parser.prototype = {
|
||||
return v;
|
||||
}, {
|
||||
assign: function(self, value, locals) {
|
||||
var key = indexFn(self, locals);
|
||||
var key = ensureSafeMemberName(indexFn(self, locals), parser.text);
|
||||
// prevent overwriting of Function.constructor which would break ensureSafeObject check
|
||||
var safe = ensureSafeObject(obj(self, locals), parser.text);
|
||||
return safe[key] = value;
|
||||
var o = ensureSafeObject(obj(self, locals), parser.text);
|
||||
if (!o) obj.assign(self, o = {});
|
||||
return o[key] = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
Vendored
+1
-1
@@ -1779,7 +1779,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||
* use the `passThrough` request handler of `when` instead of `respond`.
|
||||
*
|
||||
* Additionally, we don't want to manually have to flush mocked out requests like we do during unit
|
||||
* testing. For this reason the e2e $httpBackend automatically flushes mocked out requests
|
||||
* testing. For this reason the e2e $httpBackend flushes mocked out requests
|
||||
* automatically, closely simulating the behavior of the XMLHttpRequest object.
|
||||
*
|
||||
* To setup the application to run with this http backend, you have to create a module that depends
|
||||
|
||||
@@ -94,8 +94,10 @@ function shallowClearAndCopy(src, dst) {
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
*
|
||||
* If the parameter value is prefixed with `@` then the value of that parameter will be taken
|
||||
* from the corresponding key on the data object (useful for non-GET operations).
|
||||
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
|
||||
* from the corresponding property on the `data` object (provided when calling an action method). For
|
||||
* example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
|
||||
* will be `data.someProp`.
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
|
||||
* the default set of resource actions. The declaration should be created in the format of {@link
|
||||
|
||||
@@ -232,6 +232,13 @@ function makeMap(str) {
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParser( html, handler ) {
|
||||
if (typeof html !== 'string') {
|
||||
if (html === null || typeof html === 'undefined') {
|
||||
html = '';
|
||||
} else {
|
||||
html = '' + html;
|
||||
}
|
||||
}
|
||||
var index, chars, match, stack = [], last = html, text;
|
||||
stack.last = function() { return stack[ stack.length - 1 ]; };
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('docs.angularjs.org', function () {
|
||||
var nameInput = element(by.model('user.name'));
|
||||
nameInput.sendKeys('!!!');
|
||||
|
||||
var code = element(by.css('tt'));
|
||||
var code = element.all(by.css('tt')).first();
|
||||
expect(code.getText()).toContain('guest!!!');
|
||||
});
|
||||
|
||||
@@ -68,4 +68,11 @@ describe('docs.angularjs.org', function () {
|
||||
expect(element(by.css('.minerr-errmsg')).getText()).toEqual("Argument 'Missing' is not a function, got undefined");
|
||||
});
|
||||
});
|
||||
|
||||
describe("templates", function() {
|
||||
it("should show parameter defaults", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$timeout');
|
||||
expect(element.all(by.css('.input-arguments p em')).first().getText()).toContain('(default: 0)');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+21
-1
@@ -1647,9 +1647,11 @@ describe('jqLite', function() {
|
||||
element.triggerHandler('click');
|
||||
event = pokeSpy.mostRecentCall.args[0];
|
||||
expect(event.preventDefault).toBeDefined();
|
||||
expect(event.target).toEqual(element[0]);
|
||||
expect(event.type).toEqual('click');
|
||||
});
|
||||
|
||||
it('should pass data as an additional argument', function() {
|
||||
it('should pass extra parameters as an additional argument', function() {
|
||||
var element = jqLite('<a>poke</a>'),
|
||||
pokeSpy = jasmine.createSpy('poke'),
|
||||
data;
|
||||
@@ -1680,6 +1682,24 @@ describe('jqLite', function() {
|
||||
expect(clickOnceSpy).toHaveBeenCalledOnce();
|
||||
expect(clickSpy.callCount).toBe(2);
|
||||
});
|
||||
|
||||
it("should accept a custom event instead of eventName", function() {
|
||||
var element = jqLite('<a>poke</a>'),
|
||||
pokeSpy = jasmine.createSpy('poke'),
|
||||
customEvent = {
|
||||
type: 'click',
|
||||
someProp: 'someValue'
|
||||
},
|
||||
actualEvent;
|
||||
|
||||
element.on('click', pokeSpy);
|
||||
element.triggerHandler(customEvent);
|
||||
actualEvent = pokeSpy.mostRecentCall.args[0];
|
||||
expect(actualEvent.preventDefault).toBeDefined();
|
||||
expect(actualEvent.someProp).toEqual('someValue');
|
||||
expect(actualEvent.target).toEqual(element[0]);
|
||||
expect(actualEvent.type).toEqual('click');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -2671,6 +2671,28 @@ describe('$compile', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should update parent scope when "="-bound NaN changes', inject(function($compile, $rootScope) {
|
||||
$rootScope.num = NaN;
|
||||
compile('<div my-component reference="num"></div>');
|
||||
var isolateScope = element.isolateScope();
|
||||
expect(isolateScope.reference).toBeNaN();
|
||||
|
||||
isolateScope.$apply(function(scope) { scope.reference = 64; });
|
||||
expect($rootScope.num).toBe(64);
|
||||
}));
|
||||
|
||||
|
||||
it('should update isolate scope when "="-bound NaN changes', inject(function($compile, $rootScope) {
|
||||
$rootScope.num = NaN;
|
||||
compile('<div my-component reference="num"></div>');
|
||||
var isolateScope = element.isolateScope();
|
||||
expect(isolateScope.reference).toBeNaN();
|
||||
|
||||
$rootScope.$apply(function(scope) { scope.num = 64; });
|
||||
expect(isolateScope.reference).toBe(64);
|
||||
}));
|
||||
|
||||
|
||||
describe('attribute', function() {
|
||||
it('should copy simple attribute', inject(function() {
|
||||
compile('<div><span my-component attr="some text">');
|
||||
|
||||
@@ -735,6 +735,8 @@ describe('select', function() {
|
||||
|
||||
it('should not update selected property of an option element on digest with no change event',
|
||||
function() {
|
||||
// ng-options="value.name for value in values"
|
||||
// ng-model="selected"
|
||||
createSingleSelect();
|
||||
|
||||
scope.$apply(function() {
|
||||
@@ -743,6 +745,11 @@ describe('select', function() {
|
||||
});
|
||||
|
||||
var options = element.find('option');
|
||||
|
||||
expect(scope.selected).toEqual({ name: 'A' });
|
||||
expect(options.eq(0).prop('selected')).toBe(true);
|
||||
expect(options.eq(1).prop('selected')).toBe(false);
|
||||
|
||||
var optionToSelect = options.eq(1);
|
||||
|
||||
expect(optionToSelect.text()).toBe('B');
|
||||
@@ -987,6 +994,56 @@ describe('select', function() {
|
||||
expect(element.find('option').eq(0).prop('selected')).toBeTruthy();
|
||||
expect(element.find('option').length).toEqual(2);
|
||||
});
|
||||
|
||||
|
||||
it('should ensure that at least one option element has the "selected" attribute', function() {
|
||||
function countSelected() {
|
||||
var count = 0;
|
||||
forEach(element.find('option'), function(option) {
|
||||
count += option.getAttribute('selected') ? 1 : 0;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
createSelect({
|
||||
'ng-model': 'selected',
|
||||
'ng-options': 'item.id as item.name for item in values'
|
||||
});
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values = [{id: 10, name: 'A'}, {id: 20, name: 'B'}];
|
||||
});
|
||||
expect(element.val()).toEqual('?');
|
||||
expect(countSelected()).toEqual(1);
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.selected = 10;
|
||||
});
|
||||
// Here the ? option should disappear and the first real option should have selected attribute
|
||||
expect(element.val()).toEqual('0');
|
||||
expect(countSelected()).toEqual(1);
|
||||
|
||||
// Here the selected value is changed but we don't change the selected attribute
|
||||
scope.$apply(function() {
|
||||
scope.selected = 20;
|
||||
});
|
||||
expect(element.val()).toEqual('1');
|
||||
expect(countSelected()).toEqual(1);
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values.push({id: 30, name: 'C'});
|
||||
});
|
||||
expect(element.val()).toEqual('1');
|
||||
expect(countSelected()).toEqual(1);
|
||||
|
||||
// Here the ? option should reappear and have selected attribute
|
||||
scope.$apply(function() {
|
||||
scope.selected = undefined;
|
||||
});
|
||||
expect(element.val()).toEqual('?');
|
||||
expect(countSelected()).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1134,8 +1191,73 @@ describe('select', function() {
|
||||
browserTrigger(element, 'change');
|
||||
expect(scope.selected).toEqual(null);
|
||||
});
|
||||
|
||||
|
||||
// Regression https://github.com/angular/angular.js/issues/7855
|
||||
it('should update the model with ng-change', function() {
|
||||
createSelect({
|
||||
'ng-change':'change()',
|
||||
'ng-model':'selected',
|
||||
'ng-options':'value for value in values'
|
||||
});
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values = ['A', 'B'];
|
||||
scope.selected = 'A';
|
||||
});
|
||||
|
||||
scope.change = function() {
|
||||
scope.selected = 'A';
|
||||
};
|
||||
|
||||
element.find('option')[1].selected = true;
|
||||
|
||||
browserTrigger(element, 'change');
|
||||
expect(element.find('option')[0].selected).toBeTruthy();
|
||||
expect(scope.selected).toEqual('A');
|
||||
});
|
||||
});
|
||||
|
||||
describe('disabled blank', function() {
|
||||
it('should select disabled blank by default', function() {
|
||||
var html = '<select ng-model="someModel" ng-options="c for c in choices">' +
|
||||
'<option value="" disabled>Choose One</option>' +
|
||||
'</select>';
|
||||
scope.$apply(function() {
|
||||
scope.choices = ['A', 'B', 'C'];
|
||||
});
|
||||
|
||||
compile(html);
|
||||
|
||||
var options = element.find('option');
|
||||
var optionToSelect = options.eq(0);
|
||||
expect(optionToSelect.text()).toBe('Choose One');
|
||||
expect(optionToSelect.prop('selected')).toBe(true);
|
||||
expect(element[0].value).toBe('');
|
||||
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
|
||||
it('should select disabled blank by default when select is required', function() {
|
||||
var html = '<select ng-model="someModel" ng-options="c for c in choices" required>' +
|
||||
'<option value="" disabled>Choose One</option>' +
|
||||
'</select>';
|
||||
scope.$apply(function() {
|
||||
scope.choices = ['A', 'B', 'C'];
|
||||
});
|
||||
|
||||
compile(html);
|
||||
|
||||
var options = element.find('option');
|
||||
var optionToSelect = options.eq(0);
|
||||
expect(optionToSelect.text()).toBe('Choose One');
|
||||
expect(optionToSelect.prop('selected')).toBe(true);
|
||||
expect(element[0].value).toBe('');
|
||||
|
||||
dealoc(element);
|
||||
});
|
||||
});
|
||||
|
||||
describe('select-many', function() {
|
||||
|
||||
@@ -1183,6 +1305,7 @@ describe('select', function() {
|
||||
expect(scope.selected).toEqual([scope.values[0]]);
|
||||
});
|
||||
|
||||
|
||||
it('should select from object', function() {
|
||||
createSelect({
|
||||
'ng-model':'selected',
|
||||
|
||||
@@ -1217,6 +1217,18 @@ describe('$http', function() {
|
||||
expect(callback.mostRecentCall.args[0]).toBe('content');
|
||||
}));
|
||||
|
||||
it('should cache JSONP request when cache is provided', inject(function($rootScope) {
|
||||
$httpBackend.expect('JSONP', '/url?cb=JSON_CALLBACK').respond('content');
|
||||
$http({method: 'JSONP', url: '/url?cb=JSON_CALLBACK', cache: cache});
|
||||
$httpBackend.flush();
|
||||
|
||||
$http({method: 'JSONP', url: '/url?cb=JSON_CALLBACK', cache: cache}).success(callback);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[0]).toBe('content');
|
||||
}));
|
||||
|
||||
it('should cache request when cache is provided and no method specified', function () {
|
||||
doFirstCacheRequest();
|
||||
|
||||
|
||||
@@ -986,6 +986,32 @@ describe('$location', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should not rewrite links with `javascript:` URI', function() {
|
||||
configureService(' jAvAsCrIpT:throw new Error("Boom!")', true, true, true);
|
||||
inject(
|
||||
initBrowser(),
|
||||
initLocation(),
|
||||
function($browser) {
|
||||
browserTrigger(link, 'click');
|
||||
expectNoRewrite($browser);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should not rewrite links with `mailto:` URI', function() {
|
||||
configureService(' mAiLtO:foo@bar.com', true, true, true);
|
||||
inject(
|
||||
initBrowser(),
|
||||
initLocation(),
|
||||
function($browser) {
|
||||
browserTrigger(link, 'click');
|
||||
expectNoRewrite($browser);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should rewrite full url links to same domain and base path', function() {
|
||||
configureService('http://host.com/base/new', true);
|
||||
inject(
|
||||
|
||||
@@ -434,6 +434,17 @@ describe('parser', function() {
|
||||
expect(scope.b).toEqual(234);
|
||||
});
|
||||
|
||||
it('should evaluate assignments in ternary operator', function() {
|
||||
scope.$eval('a = 1 ? 2 : 3');
|
||||
expect(scope.a).toBe(2);
|
||||
|
||||
scope.$eval('0 ? a = 2 : a = 3');
|
||||
expect(scope.a).toBe(3);
|
||||
|
||||
scope.$eval('1 ? a = 2 : a = 3');
|
||||
expect(scope.a).toBe(2);
|
||||
});
|
||||
|
||||
it('should evaluate function call without arguments', function() {
|
||||
scope['const'] = function(a,b){return 123;};
|
||||
expect(scope.$eval("const()")).toEqual(123);
|
||||
@@ -1089,6 +1100,22 @@ describe('parser', function() {
|
||||
fn.assign(scope, 123);
|
||||
expect(scope).toEqual({a:123});
|
||||
}));
|
||||
|
||||
it('should expose working assignment function for expressions ending with brackets', inject(function($parse) {
|
||||
var fn = $parse('a.b["c"]');
|
||||
expect(fn.assign).toBeTruthy();
|
||||
var scope = {};
|
||||
fn.assign(scope, 123);
|
||||
expect(scope.a.b.c).toEqual(123);
|
||||
}));
|
||||
|
||||
it('should expose working assignment function for expressions with brackets in the middle', inject(function($parse) {
|
||||
var fn = $parse('a["b"].c');
|
||||
expect(fn.assign).toBeTruthy();
|
||||
var scope = {};
|
||||
fn.assign(scope, 123);
|
||||
expect(scope.a.b.c).toEqual(123);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -228,6 +228,16 @@ describe('HTML', function() {
|
||||
.toEqual('<p> 10 < <span>100</span> </p>');
|
||||
});
|
||||
|
||||
it('should accept non-string arguments', function() {
|
||||
expectHTML(null).toBe('');
|
||||
expectHTML(undefined).toBe('');
|
||||
expectHTML(42).toBe('42');
|
||||
expectHTML({}).toBe('[object Object]');
|
||||
expectHTML([1, 2, 3]).toBe('1,2,3');
|
||||
expectHTML(true).toBe('true');
|
||||
expectHTML(false).toBe('false');
|
||||
});
|
||||
|
||||
describe('htmlSanitizerWriter', function() {
|
||||
/* global htmlSanitizeWriter: false */
|
||||
if (angular.isUndefined(window.htmlSanitizeWriter)) return;
|
||||
|
||||
Reference in New Issue
Block a user