๊ฐ๋๊ธฐ 0.8์ ์คํํ ๋ ๋ฌธ์ ๊ฐ ์๊ณ ์๋์ผ๋ก ๋ถํธ์คํธ๋ฉ๋ ์ฑ์์ ๊ฐ๋๊ธฐ๋ฅผ ์คํํ๋ ๋ฐ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
๋ค์์ ์ ํ๋ฆฌ์ผ์ด์
๋ฐ ์ค์ ์ ๊ด๋ จ ๋ถ๋ถ์ ๋ณด์ฌ์ฃผ๋ ๋ช ๊ฐ์ง ์ค๋ํซ์
๋๋ค.
index.html:
<script type="text/javascript" data-main="js/main" src="js/libraries/require.js"></script>
js/main.js:
require({
shim: {
.... snip ..
}
},
['require', ..... ], function(require) {
return require(['bootstrap']);
๋ถํธ์คํธ๋ฉ.js:
var appElm = $('html');
appElm.addClass('ng-app'); // this
angular.bootstrap(document, ['app']);
์ด๊ฒ์ ์ค์ ๋ก ์๋ํ๋ฉฐ Angular๋ ์ฌ๋ฐ๋ฅธ ๋ฐฉ์์ผ๋ก ์์๋ฉ๋๋ค. html ์์ค๋ฅผ ๋ณผ ๋ html ์์์ ๋ํ๋๋ ng-app ์์ฑ์ ๋ณผ ์ ์์ต๋๋ค. ๊ฐ๋๊ธฐ๋ ๊ทธ๋ ๊ฒํ์ง ๋ชปํ ์ ์์ต๋๊น?
๊ฐ๋๊ธฐ ์ํ ๊ตฌ์ฑ์ ์ฌ์ฉํ๊ณ ๊ด๋ จ ๊ฐ์ ๋ด ์ฑ์ ๋ง๊ฒ ๋ณ๊ฒฝํ์ต๋๋ค.
specs: [
'../src/test/javascript/protractorSpec/*_spec.js',
],
baseUrl: 'http://localhost:8080/Viewer/',
rootElement: 'html',
์ํ ํ ์คํธ:
describe('List SMC Charts', function() {
var ptor= protractor.getInstance();
beforeEach(function() {
ptor.get('#/smcChart');
});
it('list, no filter', function() {
var selectOption = ptor.findElement(ptor.By.input("selectOption"));
});
});
์ด ํ ์คํธ๋ฅผ ์คํํ๋ฉด ๋ ๊ฐ์ง ์ค๋ฅ๊ฐ ๋ํ๋ฉ๋๋ค.
1) List SMC Charts list, no filter
Message:
Error: Angular could not be found on the page http://localhost:8080/Viewer/#/smcChart
Stacktrace:
Error: Angular could not be found on the page http://localhost:8080/Viewer/#/smcChart
at /usr/local/lib/node_modules/protractor/lib/protractor.js:392:15
๊ทธ๋ฆฌ๊ณ
2) List SMC Charts list, no filter
Message:
TypeError: Cannot call method 'input' of undefined
Stacktrace:
TypeError: Cannot call method 'input' of undefined
at null.<anonymous> (/Users/........//Viewer/src/test/javascript/protractorSpec/some_spec.js:9:55)
์ฒซ ๋ฒ์งธ ์ค๋ฅ๊ฐ ๊ฐ์ฅ ์ค์ํ ๊ฒ ๊ฐ์ง๋ง ๋ง์ง๋ง ์ค๋ฅ๋ ๊ฝค ํฅ๋ฏธ๋กญ์ต๋๋ค. ptor.By.input ์ค์ด ์๋ํ์ง ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
์ด๋ค ์์ด๋์ด?
Angular ์ฝ๋๊ฐ ํ์ด์ง์ ํฌํจ๋์ด ์์ต๋๊น? shim, bootstrap ๋ฐ ๊ธฐํ ํญ๋ชฉ์ด ํ์ํ ๊ฒ ๊ฐ์ง๋ง angular.min.js
์ด ํ์๋์ง ์์ต๋๋ค.
'app': {
deps: ['libraries/angular', 'libraries/angular-resource', 'libraries/angular-strap']
},
์์ ์ค๋ํซ์ shim{} ์์ ๋ด๋ถ์ ์์ต๋๋ค. ์ ์ ๋ณ๋ก ๊ด๋ จ์ด ์๋ ๊ฒ ๊ฐ์์ ์ค๋ํซ์์ ์ ์ธํ์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๊ฒ์ด angular.js๊ฐ ๋ก๋๋๋ ๋ฐฉ์์ ๋๋ค.
์๋ ,
์, ๊ฐ๋๊ธฐ๋ ํ์ฌ ng-app์ ์์ํ๋ฏ๋ก ์๋์ผ๋ก ๋ถํธ์คํธ๋ฉ๋ ์ฌ์ดํธ์๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ์ด ๋ฌธ์์ ๋ํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ด ๊ณง ๋ฌธ์ํ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
์ ๋ฐ์ดํธ๋ฅผ ์ํ ํ์ธ thx. ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ฐพ์์ผ๋ฉด ์ด ๋ฌธ์ ์ ๊ฒ์ํ ์ ์์ต๋๊น?
์๋ต์ด ๋๋ ค์ ์ฃ์กํฉ๋๋ค. ๋ค์์ ์ํํ์ฌ ์์ ํ ์ ์์ด์ผ ํฉ๋๋ค.
describe('List SMC Charts', function() {
var ptor= protractor.getInstance();
beforeEach(function() {
ptor.driver.get('#/smcChart'); // Just use the plain old webdriver here, it won't complain if Angular isn't loaded yet.
});
it('list, no filter', function() {
var selectOption = ptor.findElement(ptor.By.input("selectOption"));
});
});
ptor ๋ณ์๋ฅผ ์ด๋๊ฐ์ ์ค์ ํ์ต๋๊น? ์: var ptor = protractor.getInstance()
?
์ด์ ๋ํ ์ ๋ฐ์ดํธ๊ฐ ์์ต๋๊น? ์ ์ํ ์์ ์ฌํญ์ด ์๋ํ์ง ์์ต๋๋ค(๊ฐ๋๊ธฐ@0.10.0, ๊ฐ๋@1.0.7).
๊ฐ๋๊ฐ ์๋ ๋ก๊ทธ์ธ ํ๋ฉด๊ณผ ์๋์ผ๋ก ๋ถํธ์คํธ๋ฉ๋ ์ฑ์ด ์๋ requirejs์ ์ด์ํ ์กฐํฉ์ด ์์ต๋๋ค.
describe('User levels', function() {
var ptor, driver;
var findByName = function (name) {
return driver.findElement(protractor.By.name(name));
};
describe('Editors', function() {
ptor = protractor.getInstance();
driver = ptor.driver;
it('Can log in', function() {
driver.get('http://localhost:8000/');
findByName('login').sendKeys('test_editor');
findByName('pass').sendKeys('rubbish');
driver.findElement(protractor.By.css('button[type="submit"]')).click();
}, 20000);
it('Shows the homepage', function() {
var el = ptor.findElement(protractor.By.css('h1[field="pageTitle"]'));
expect(el.getText()).toEqual('Welcome to Home');
}, 30000);
});
});
๊ทธ๋ฆฌ๊ณ ๋ด main.js
:
require([
'angular',
'app'
], function (angular, app) {
'use strict';
var body = document.body, name = app.name;
body.setAttribute('ng-app', name);
body.setAttribute('id', 'ng-app');
angular.bootstrap(body, [name]);
});
๋ก๊ทธ์ธ ๋ถ๋ถ์ด ์๋ํ๊ณ UnknownError: javascript error: angular is not defined
๋ก ๊ฐ๋๊ธฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ธฐ ์ ์ ๋ณดํธ๋ ํ์ด์ง๊ฐ ๋ก๋๋๋ ํ๋์๋ฅผ ๋ณผ ์ ์์ต๋๋ค. driver.get('...')
๋ฐ ptor.get('...')
๋ก ํ์ด์ง๋ฅผ ๋ค์ ๋ก๋ํ๋ ค๊ณ ์๋ํ์ง๋ง ์๋ฌด ๊ฒ๋ ์๋ํ์ง ์์ต๋๋ค.
๋ชจ๋ ํํธ/์กฐ์ธ/์์ ๊ฐ์ฌํ ๋ฐ์์ต๋๋ค.
@magnetised ๋ค๋ฅธ ํ ์คํธ๋ฅผ ์๋ํ๊ธฐ ์ ์ ๋ก๊ทธ์ธ์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ผ๊ณ ํ ์คํธ์ ์๋ฆฌ๋ ๋ฐฉ๋ฒ์ด ํ์ํฉ๋๋ค. Angular๊ฐ ํ์ด์ง์ ๋ก๋๋๊ธฐ ์ ์ ptor.findElement๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํ๋ ๊ฒ ๊ฐ์ต๋๋ค.
'๋ก๊ทธ์ธ ๊ฐ๋ฅ' ์ฌ์์ ์ฑ์ ์ผ๋ถ ์กฐ๊ฑด์ ํ์ธํ๋ ๋๊ธฐ ๋ฌธ์ผ๋ก ๋๋์ผ ํฉ๋๋ค. ์ฌ๊ธฐ์์ ์์ ๋ฅผ ํ์ธํ์ญ์์ค - https://github.com/angular/protractor/blob/master/spec/login/viaConfigConf.js
@juliemr ํ๋ฅญํฉ๋๋ค. ์๋ฒฝํ๊ฒ ์๋ํฉ๋๋ค.
๊ฐ๋๊ธฐ๊ฐ "๊ฐ๋๊ฐ ๋ก๋๋ ๋๊น์ง ๋๊ธฐ"๋ฅผ ์ํํ๋ค๋ ๊ฒ์ ์ดํดํ์ง๋ง ๋ชจ๋ ๊ฒฝ์ฐ๋ฅผ ๋ค๋ฃฐ ์๋ ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
๋ค์ ํ๋ฒ ๊ฐ์ฌํฉ๋๋ค.
๋๋ ์ ํํ ๊ฐ์ ์ค์ ์ ๊ฐ์ง๊ณ ์์ต๋๋ค. @juliemr ์๋ฃจ์ ์ ๊ณต์ ํ์๊ฒ ์ต๋๊น? ๋น์ ์ ๋๊ธฐ ์กฐ๊ฑด์ ๋ฌด์์ ๋๊น? ๊ฐ์ฌ ํด์!
@jorgenfb ์ ๋๊ธ์ด ์ ๋ฅผ ๊ฒจ๋ฅํ ๊ฒ์ธ์ง ๊ถ๊ธํฉ๋๋ค.
๋ด ์๋ฃจ์ ์ ์ต๊ทค๋ฌ ๋ผ์ฐํฐ๊ฐ ๋ถํ ๋๊ณ ๋ก๊ทธ์ธ ์์คํ ์ด ๋ฆฌ๋๋ ์ ํ๋ ๋ฒ ์ด '/'์์ ๊ธฐ๋ณธ ๊ฒฝ๋ก(์ด ๊ฒฝ์ฐ '/#view')๋ก URL์ ๋ณ๊ฒฝํ๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ๊ณผ ๊ด๋ จ๋์์ต๋๋ค.
๋ชจ๋ ํ ์คํธ์์ ์ฌ์ฌ์ฉํ ์ ์๋ ํจ์๋ก ๋ง๋ค์์ต๋๋ค.
var waitUntilLoaded = function() {
ptor.wait(function() {
return ptor.driver.getCurrentUrl().then(function(url) {
return /view/.test(url);
});
});
};
์ด๊ฒ์ด ๋น์ ์๊ฒ ํจ๊ณผ๊ฐ ์์์ง ๋ชจ๋ฅด๊ฒ ์ง๋ง ๋ผ์ฐํ ๊ตฌ์ฑ์ ๋ฐ๋ผ ๋ค๋ฆ ๋๋ค.
@juliemr ๊ฐ์ฌํฉ๋๋ค, ๋งค๋ ฅ์ฒ๋ผ ์๋ํฉ๋๋ค :)
Angular๊ฐ ๋งค์ฐ ํ๋ฒํ๊ฒ ๋ก๋๋์ด ์คํ ์ค์์๋ ๋ถ๊ตฌํ๊ณ ๊ฐ๋๊ธฐ๊ฐ Angular๋ฅผ ์ฐพ์ง ๋ชปํ๋ ๋น์ทํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค(SO: http://stackoverflow.com/questions/19391813/protractor-fails-to-find-angular). ์ฌ์ด ์๋ฃจ์
(Angular๋ฅผ ์ฐพ์ผ๋ ค๊ณ ์๋ํ์ง ์๊ณ ํ
์คํธ)์ด ์ฐ๋ฆฌ๊ฐ ๊ฐ์ผ ํ ๋ฐฉํฅ์
๋๊น? Angular ์ฑ์ ํ
์คํธํ๊ธฐ ์ํด ํน๋ณํ ์ ์๋ Protractor์ ๋ชฉ์ ์ ๋ฌด๋๋จ๋ฆฌ๋ ๊ฒ ๊ฐ์ต๋๋ค.
ํธ์ง: ๋ํ ์ด ๋ฌธ์ ์์ ์ ์๋ ์๋ฃจ์
์ด ์ ์๊ฒ ์ ํฉํ์ง ์๋ค๋ ์ ์ ์ ์ํด์ผ ํฉ๋๋ค.
๋ ๋์ ๊ฐ๋์ ์๋ ๋ถํธ์คํธ๋ฉ ๋ฌธ์ํ๋ฅผ ์ํด +1
์ด๊ฒ์ ์ฌ์ ํ โโ๊ด๋ จ์ด ์์ต๋๊น?
๊ทธ ์ดํ๋ก ์๋๋ฅผ ํฌ๊ธฐํ๊ณ ๋ค์ ๋์๊ฐ์ ๋ฌด์ธ๊ฐ๊ฐ ๋ณ๊ฒฝ๋์๋์ง ํ์ธํ ์๊ฐ์ด ์๊ธฐ ๋๋ฌธ์ ์์งํ ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ๊ทธ ์ดํ๋ก ๋ฒ์ ๋ณ๊ฒฝ ์ฌํญ์ ๊ณ ๋ คํ ๋ ์ด๊ฒ์ ๋ซ์ผ๋ ค๋ ๊ฒฝ์ฐ ๋ ผ์ํ์ง ์์ ๊ฒ์ ๋๋ค. ๋์ค์ ์ธ์ ๋ ์ง ๋ค์ ์ด ์ ์์ต๋๋ค.
์ ๋ ์ค๋ ๋ฐฉ๊ธ ๊ฐ๋๊ธฐ๋ฅผ ์ฌ์ฉํ๊ธฐ ์์ํ๊ณ ์ฐ๋ฆฌ ์ฑ์ require.js๋ฅผ ์ฌ์ฉํฉ๋๋ค... ์๋์ผ๋ก ๋ถํธ์คํธ๋ฉ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์คํธํ๋ ๊ฒ๊ณผ ๊ด๋ จํ์ฌ ์ ๋ฐ์ดํธ๊ฐ ์๋ค๋ฉด ์ฐ์ ๊ด์ฌ์ด ์์ต๋๋ค. :-)
๊ฐ์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. Angular ์ฑ ์ธ๋ถ์ ๋ก๊ทธ์ธ ํ์ด์ง๊ฐ ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์ต๋๋ค. ์ด์ ์๊ฒฌ์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ค๋๋ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๋ฐ ์ข ๋ฅ์ ์๋๋ฆฌ์ค๋ฅผ ํ ์คํธํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ํ ์ ๋ฐ์ดํธ๋ ๋ฌธ์๋ฅผ ๋งค์ฐ ์ํฉ๋๋ค.
@hankduan ๋ค, ๊ทธ๋ ๊ฒ ์๊ฐํฉ๋๋ค.
์ ๋ ์ง๊ธ ์ด ๋ฌธ์ ๋ก ์ด๋ ค์์ ๊ฒช๊ณ ์์ต๋๋ค. ๋ฐฐ๊ฒฝ์ ๋น์ทํฉ๋๋ค. RequireJS๋ฅผ ํตํด ์ข ์์ฑ์ ๊ด๋ฆฌํ๋ ๋ ํฐ ์์ฉ ํ๋ก๊ทธ๋จ์ด ์์ต๋๋ค.
window.name='NG_DEFER_BOOTSTRAP!';
& angular.resumeBootstrap()
๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฏธ ์ฑ์ ์
๋ฐ์ดํธํ์ง๋ง ์ฌ์ ํ ์๋ํ์ง ์์ต๋๋ค.
์ด ๋ง์, ๋๋ ๋ค์์ ๋ฌด์์ํด์ผํ ์ง ๋ชจ๋ฅด๊ฒ ๋ค. ์ถ๊ฐ ์กฐ์ฌ๋ฅผ ์ํด https://github.com/knalli/protractor-with-deferred-bootstrap ์์ ํ ์คํธ ์ผ์ด์ค๋ฅผ ์ค์ ํ์ต๋๋ค.
git clone https://github.com/knalli/protractor-with-deferred-bootstrap.git
cd protractor-with-deferred-bootstrap
npm install
npm run-script webdriver
(์
๋ ๋ ์๋ฒ)npm run-script server
(๋ฏธ๋ AJS ์ฑ์ ์ ๊ณตํ๋ ๋ฐ๋ชจ ์ฑ ์๋ฒ)npm run-script e2e-test
(๊ฐ๋๊ธฐ ์คํ...)๊ฒฐ๊ณผ๋
Using the selenium server at http://localhost:4444/wd/hub
Login: Click
Login: Waiting...
A Jasmine spec timed out. Resetting the WebDriver Control Flow.
The last active task was:
unknown
F
Failures:
๋ค์ด์ด
UnknownError: unknown error: [ng:btstrpd] App Already Bootstrapped with this Element '
๋ง์ง๋ง ์ค๋ฅ๋ ์ฝ๊ฐ ์ด์ํ๊ณ ์คํด์ ์์ง๊ฐ ์๊ธฐ๊น์ง ํฉ๋๋ค. ํ ์คํธ๊ฐ ์ํ๋์์ต๋๋ค(์์์ด ์ ์์ด๊ณ ๋ธ๋ผ์ฐ์ ์์ ๋ณผ ์ ์๋ ํ ์ฑ๊ณต์ ์).
๊ต์ฐจ ํ์ธ: ๋ฐ๋ชจ ์ฑ์ ์๋ ํฌํจ์ ์ฌ์ฉํ์ฌ ์ง์ฐ๋์ง ์์ ๋ถํธ์คํธ๋ํ์ผ๋ก ์ ํํ๋ฉด(์ด ๋ณํ์ no-deferred
๋ถ๊ธฐ์์ ์ฌ์ฉ ๊ฐ๋ฅ) ์ฑ๊ณต์ ์ผ๋ก ์๋ํฉ๋๋ค.
๋๋ ์ด๋ฏธ ์์ "ํด๊ฒฐ์ฑ "์ ์๋ํ์ต๋๋ค.
protractor.getInstance()
๋ฐ ignoreSynchronization
์ฌ์ฉ --> ์ฐจ์ด ์์clientSideScripts.js/waitForAngular
--> ์ฐจ์ด ์์์ด์ ์ฒซ ๊ฐ๋๊ธฐ ํ ์คํธ๋ฅผ ์์ฑํ๊ณ knalli์ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
@knalli ์์ ๋ฅผ ์์ฑํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ๋ช์ผ๋ค์ ๋ด์ผ๊ฒ ๋ค
๊ทธ๋์ ๋ด ๊ฐ๋๊ธฐ๋ ๋ชจ๋ ์์
์ ํ
์คํธํฉ๋๋ค(ํ๋์ ์คํจ๋ฅผ ์ ์ธํ๊ณ ๋ ๋ด ์๋ชป์
๋๋ค :-P ).
์ด๊ฒ์ด ๋์์ด ๋๋์ง ์ฌ๋ถ๋ฅผ ์๋ ค์ฃผ์ญ์์ค.
๋ด ์ค์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๊ฐ๋๊ธฐ ๋ฒ์ :
๋ฒ์ 1.0.0-rc4
์
๋ ๋ ๋
๋ฆฝ ์คํํ:
์ต์ ์
๋๋ค
ํฌ๋กฌ ๋๋ผ์ด๋ฒ:
์ต์ ์
๋๋ค
/*jshint expr: true*/
/*global browser: true*/
var TEST_DIR = '../tests/';
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub'
, specs : [ '../tests/e2e/_protractor.js' ]
, capabilities: {'browserName': 'chrome' }
, chromeDriver: './chromedriver' // <---- I remember not being able to get chromedriver working
// for some reason, so I just downloaded the executable and put it
// in my config directory
, onPrepare: function() {
"use strict";
browser.driver.get('http://localhost:5000/register');
browser.driver.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
return /\#\/registration/.test(url);
});
});
}
// chromeOnly: true,
// baseUrl: 'http://localhost:5000/register'
, jasmineNodeOpts: {
showColors: true
}
};
/*jshint expr: true, undef: true */
/* global element: true, by: true, browser: true, model:true */
(function(){
"use strict";
var RegistrationPage = function(){
browser.driver.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
return /\#\/registration/.test(url);
});
});
};
var account = new RegistrationPage();
account.elem = element.all(by.name('account_exists'));
account.accountNumber = element(by.name('account_number'));
account.submitAccountButton = element(by.css('#account_input button'));
account.selectHasAccount = function(){
account.elem.get(0).click();
return browser.getCurrentUrl();
};
account.enterAccountNumber = function(keys){
account.selectHasAccount().then(function(){
account.accountNumber.sendKeys(keys);
return account.submitAccountButton.click()
.then(function(){
return browser.getCurrentUrl();
});
//this wont log because already returned ^
// console.log('need to return again');
});
};
account.noAccount = function(){
account.elem.get(1).click();
};
describe('registration page', function() {
it("will survive", function(){
var registrationPage = new RegistrationPage()
, title = browser.getTitle();
expect(title).toBe('Web Shop | Homepage');
});
it('will change route on account_exists radiobox click', function(){
var elem = element.all(by.name('account_exists'))
, _url;
elem.get(0).click()
.then(function(){
_url = browser.getCurrentUrl().then(function(_url){
expect(_url).toBe('http://localhost:5000/register#/registration/has');
});
})
.then(function(){
elem.get(1).click().then(function(){
_url = browser.getCurrentUrl();
expect(_url).toBe('http://localhost:5000/register#/registration/no');
});
});
});
describe('navigate the hasAccount path:', function(){
it('will click hasAccount which will cause a route change.', function () {
expect(account.selectHasAccount()).toBe('http://localhost:5000/register#/registration/has');
});
it("will submit a valid account number", function(){
expect(account.enterAccountNumber('1231231231')).toBe('http://localhost:5000/register#/user');
});
});
it('will click noAccount which will cause a route change.', function () {
var _url;
account.noAccount();
_url = browser.getCurrentUrl();
expect(_url).toBe('http://localhost:5000/register#/registration/no');
});
});
})();
var pathToJQuery
if('querySelector' in document
&& 'localStorage' in window
&& 'addEventListener' in window) {
//ie9+ and all browsers
pathToJQuery = 'lib/jquery/dist/jquery'
} else {
//ie8 and ie7
pathToJQuery = 'lib/jquery-1.7.2.min'
}
requirejs.config({
paths : {
// Paths
"reg": 'js/registration'
, "com": 'js/common'
, "init": 'js/init'
, "regD": 'js/registration/directives'
, "regC": 'js/registration/controllers'
, "regS": 'js/registration/services'
, "regM": 'js/registration/modules'
//
// Frameworks
, "angular": "lib/angular/angular"
, "angular.animate" : "lib/angular-animate/angular-animate"
, "jquery": pathToJQuery //< = == v long story; don't judge me.
, "jqueryMigrate" : "lib/jquery-migrate/jquery-migrate"
, "mocks": "lib/angular-mocks/angular-mocks"
, "ui_router": "lib/angular-ui-router/release/angular-ui-router"
, "Q": "lib/q/q"
// Init
, "apps_init" : 'js/init/apps_init'
, "apps_bootstrap" : 'js/init/apps_bootstrap'
// , "jquery_bootstrap" : 'js/init/jquery_bootstrap'
, "registration": "js/init/registration"
// Common
// Registration
, "registration": "js/registration/registration"
// Controllers
, "account.controller" : 'js/registration/controllers/account.controller'
, "user.controller" : 'js/registration/controllers/user.controller'
// Directives
, "account.directive" : 'js/registration/directives/account.directive'
// Services
, "user.service" : "js/registration/services/user.service"
}
, shim: {
'angular': {
exports : 'angular'
}
, 'angular.animate' : {deps: ['angular']}
, 'jqueryMigrate' : {deps: ['jquery']}
, 'ui_router' : ['angular']
, 'mocks': { deps: ['angular'], 'exports': 'angular.mock' }
}
});
if( pathToJQuery === 'lib/jquery/dist/jquery' ){
requirejs([
'angular' , 'jquery' , 'jqueryMigrate', 'init/jquery_bootstrap', 'init/apps_bootstrap' ]
, function(angular, jquery, jquery_bootstrap, apps_bootstrap) {
}
);
} else {
requirejs([
'angular' , 'jquery' , 'jqueryMigrate', 'init/jquery_bootstrap', 'init/apps_bootstrap' ]
, function(angular, jquery, jqueryMigrate, jquery_bootstrap, apps_bootstrap) {
}
);
}
console.log("working");
<!DOCTYPE html>
<html xmlns:ng="http://angularjs.org"
xmlns:fab="http://angularjs.org"
xmlns:ui ="http://angularjs.org"
lang = "en" class = "no-js" >
<head >
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<meta charset="utf-8">
<link rel="shortcut icon" type="image/x-icon" media="all" href="/images/favicon.ico" />
<script type="text/javascript" src="lib/modernizr.js"></script>
<!-- Default ie theme files:-->
<!--[if lte IE 9]><script type="text/javascript">console = {}; console.log = function(){};</script><![endif]-->
<!--[if IE 8]><link type="text/css" rel="stylesheet" href="build/css/ie8/theme.css" /><![endif]-->
<!--[if IE 7]><link type="text/css" rel="stylesheet" href="build/css/ie7/theme.css" /><![endif]-->
<!-- Normal styles -->
<link type="text/css" href="css/style.css" rel="stylesheet">
<!--[if IE 8]> <link type="text/css" rel="stylesheet" href="build/css/ie8/ie_8.css" /> <![endif]-->
<!--[if IE 7]> <link type="text/css" rel="stylesheet" href="build/css/ie7/lte_ie7.css"/> <link type="text/css" rel="stylesheet" href="build/css/ie7/ie_7.css" /><![endif]-->
<script data-main="main" src="lib/requirejs/require.js"></script>
</head >
<body class = "page-homepage language-en" >
<div id = "wrapper" >
<div id = "page" data-currency-iso-code = "USD" >
{{> _header}}
<a href = "#skip-to-content" class = "skiptocontent" >Skip to content</a >
<a href = "#skiptonavigation" class = "skiptonavigation" >Skip to navigation menu</a >
<!--[if lte IE 10]> <p class="browsehappy" style="margin-top: 10px; ">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p><![endif]-->
<main ng-cloak id = "content" role = "main" class = "page_main <!--[if IE7]>ng-cloak<![endif]-->" >
</main>
</div >
</div >
</body >
</html >
(function(){
"use strict";
// node requires
//==============================
// express-hbs
// express
//
const express = require('express')
, portN = 5000
, hbs = require('express-hbs')
, app = express()
, path = require('path')
, Server = require('./server')
, bodyParser = require('body-parser')
, serveStatic = require('serve-static')
, session = require('express-session')
, router = express.Router()
, logger = require('morgan')
, methodOverride = require('method-override')
, errorhandler = require('errorhandler')
// view paths
//==============================
//
//
//
, viewsD = __dirname + '/views/'
, partialsD = viewsD + 'partials/'
, layoutsD = viewsD + 'layouts/'
, testsD = __dirname + '/tests/'
, publicD = __dirname + '/public/'
, styleguideD = publicD+ '/styleguide/'
, defaultF = layoutsD + 'default.hbs';
// Express setup.
//==============================
//
//
//
app.use(express.static(publicD))
.use(bodyParser())
.use(logger('dev'))
.use(methodOverride())
.use(serveStatic(path.join(__dirname, 'public')))
.use(errorhandler());
app.set('view engine', 'hbs')
.set('port', process.env.PORT || portN)
.set('cache', false)
.set('views', viewsD);
app.engine('hbs', hbs.express3({
partialsDir: partialsD,
defaultLayout: defaultF,
layoutsDir: layoutsD
}));
var indx = {};
app.get( '/' ,function (req, res) {
res.render(partialsD + '_home.hbs', indx);
});
app.get( '/register' ,function (req, res) {
res.render(partialsD + '_register.hbs', indx);
});
new Server(app);
})();
module.exports = function(app){
"use strict";
var http = require('http');
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
};
module.exports = function (app) {
"use strict";
var http = require ('http');
http.createServer (app).listen (app.get ('port'),function () {
console.log ('Express server listening on port ' + app.get ('port'));
}).on ('error', function (e) {
console.log ("got error: " + e.message);
});
};
"dependencies": {
"body-parser": "^1.2.1",
"bower": "^1.3.2",
"errorhandler": "^1.0.1",
"express": "^4.3.1",
"express-hbs": "^0.7.9",
"express-session": "^1.2.0",
"node-dev": "^2.3.0"
},
"scripts": {
"start": "node --harmony $* ./node_modules/.bin/node-dev controller.js",
},
"devDependencies": {
"browserstack-webdriver": "^2.41.1",
"grunt-protractor-runner": "^1.0.0",
"method-override": "^1.0.2",
"morgan": "^1.1.1",
"protractor": "^0.23.1",
"requirejs": "^2.1.11",
"serve-static": "^1.1.0",
},
"author": "Andrew Luhring"
config/
protractor.config.js
chromedriver
server.js
controller.js
public/
main.js
js/
lib/
tests/
e2e/
_protractor.js <=== test file.
views/
layouts/
default.hbs
partials/
_head.hbs
_register.hbs <=== file being tested.
๊ทธ๊ฒ์ด ๋์ ์ ์ฒด ์ค์ ์ด๋ผ๊ณ ํ์ ํฉ๋๋ค. ๋ด๊ฐ ์ฅํฉํ๊ฒ ๊ฐ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ค. ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค.
๋ด๊ฐํด์ผ ํ ์ผ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
https://github.com/mikaraento/protractor-reuse/blob/master/index.js ์ฐธ์กฐ
ํฌ๋กฌ ๋ฐ ํ์ด์ดํญ์ค ๋ธ๋ผ์ฐ์ ์์ ์๋ฒฝํ๊ฒ ์คํ๋๋ ๊ฐ๋๊ธฐ ์คํฌ๋ฆฝํธ๊ฐ Internet Explorer์์ ํญ์ ์คํํ ์ ์๋ ์ด์ ๋ฅผ ์ ์ ์์ต๋๊น?
๋ค๋ฅธ ๋ธ๋ผ์ฐ์ ์์๋ ๋์ผํ ์คํฌ๋ฆฝํธ๊ฐ ์๋ฒฝํ๊ฒ ์คํ๋์ง๋ง Internet Explorer์์๋ ํญ์ ๋์ผํ๊ฒ ์คํํ ์ ์์ต๋๋ค. ๋ค์๊ณผ ๊ฐ์ด ์๊ฐ ์ด๊ณผ ์์ธ์ ๊ฐ์ ์์ฃผ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค.
๋ฉ์์ง: '์๊ฐ ์ด๊ณผ: ์ฌ์์ด ์๋ฃ๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ 30000๋ฐ๋ฆฌ์ด ํ์ ์๊ฐ ์ด๊ณผ๋จ',
์ถ์ : { ์คํ: ์ ์๋์ง ์์ } } ]
๋ํ Internet Explorer์์ ์์๊ฐ ๊ฐ์ง๋์ง ์๋ ๊ฒฝ์ฐ๋ ์์ต๋๋ค.
๊ธฐ๋ณธ ์๊ฐ ๊ฐ๊ฒฉ์ defaultTimeou tInterval:1000000 ์ผ๋ก ์ฌ์ฉํ์ง๋ง ๋ด ์คํฌ๋ฆฝํธ์์ Internet Explorer์์ ์คํํ๋ ๋์ ์์์ ์ธ๊ธํ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค...
์๋ฌด๋ ์ด๊ฒ์ ๋ํด ๋๋ฅผ ๋์ธ ์ ์์ต๋๊น?
๋ฌธ์ ์ธ์ฌ,
์ฐํ ์ ์ถ์ถ
์๋ ๋ถํธ์คํธ๋ํ์๋ง ๋ฌธ์ ๊ฐ ์๋ ๊ฒ ๊ฐ์ง ์์ต๋๊น? ์๋๋ฉด ์ด๋ฏธ Chrome ๋ฐ Firefox์์ ์๋ ๋ถํธ์คํธ๋ฉ๋ ์ฑ์ ์คํํ ์ ์์ต๋๊น?
@hankduan ์ด๋ฏธ ํ ์คํธ ์ผ์ด์ค๋ฅผ ์ดํด๋ณด์ จ๋์? ์ฐ๋ฆฌ๊ฐ ๋์ธ ์ ์๋ ๊ฒ์ด ์์ต๋๊น?
๋๋ ๊ฐ๋๊ธฐ(https://github.com/angular/protractor/pull/1155)์์ ๋ถํธ์คํธ๋ํ์ด ์๋ํ๋ ๋ฐฉ์์ ๋ณ๊ฒฝํ๋ CL์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ฃผ์ ๋ชฉ์ ์ ๋ค๋ฅธ ์ฌ๋ฌ ๋ฌธ์ ๋ฅผ ์์ ํ๋ ๊ฒ์ด์ง๋ง ๋ถํธ์คํธ๋ฉ ๋ฐฉ์์ ๋ฐ๋ผ ์๋ ๋ถํธ์คํธ๋ฉ์ผ๋ก ์์ (๋๋ ์ต์ํ ๋์)๋์ด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค. ๊ทธ๋ฌ๋ ํ ์คํธํ๊ธฐ ์ํด ์๋ ๋ถํธ์คํธ๋ฉ์ ์ค์ ํ ์ ์ด ์์ผ๋ฏ๋ก ์ด๊ฒ์ ์ด๋ก ์ ์ธ ๊ฒ์ ๋๋ค.
์ด PR์ ๊ฐ๋๊ธฐ ๋ณ๊ฒฝ๊ณผ Angular ๋ณ๊ฒฝ์ด ํ์ํ๊ธฐ ๋๋ฌธ์ ๋จ๊ธฐ๊ฐ์ ๊ณต๊ฐ๋์ง ์์ ์ ์์ต๋๋ค.
์ด ๋ฌธ์ ๋ Protractor์ ํจ๊ป ์ฌ์ฉํ๊ธฐ ์ํด Angular๋ฅผ ์๋์ผ๋ก ๋ถํธ์คํธ๋ฉํ๋ ๋ฐฉ๋ฒ์ ์ฐพ์ ๋ ๊ฐ์ฅ ๋ง์ด ๊ฒ์๋ ๊ฒฐ๊ณผ์ธ ๊ฒ ๊ฐ์ต๋๋ค. ๊ทธ๋์ ๋ค๋ฅธ ์๋ฃจ์ ์ ์ ์ํ๊ธฐ์ ์ฌ๊ธฐ๋งํผ ์ข์ ๊ณณ์ด๋ผ๊ณ ์๊ฐํ์ต๋๋ค. ์ด๊ฒ์ ์ ์ฒด ์ธํฐํ์ด์ค๊ฐ Angular ์ฑ์ธ ๊ฒฝ์ฐ์ ์๋ํฉ๋๋ค(์์ ์์ ๊ฐ์ด ๋ก๊ทธ์ธ ํ div์์ ๋ถํธ์คํธ๋ํ๊ณผ ๊ฐ์ ์์ ์ ์ํํ๋ ๋์ ).
Angular๊ฐ ๋ก๋๋ ๋๊น์ง ๊ฐ๋๊ธฐ์ ํ ์คํธ๋ฅผ ์ง์ฐํ ์ ์์ง๋ง ๊ณต์ Angular ๋ฌธ์์์๋ ๊ฐ๋๊ธฐ๋ก ์๋ ๋ถํธ์คํธ๋ํ์ ์ํํ ์ ์๋ค๊ณ ๋ช ์ํ๊ณ ์์ต๋๋ค. ๋๋ ๊ณต์ ๋ฌธ์๊ฐ ๊ธ์งํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๋ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ํผํ๋ ๊ฒฝํฅ์ด ์์ต๋๋ค.
์ ๋ Grunt๋ฅผ ์ฌ์ฉํ์ฌ ๋ด ์ฝ๋๋ฅผ ๋จ์ผ JS ํ์ผ๋ก ์ฐ๊ฒฐ/Uglifyํ๊ณ ์์ต๋๋ค. ์ด ํ์ผ์ index.html์์ ์ฐธ์กฐํ๋ ์ ์ผํ ์คํฌ๋ฆฝํธ์ด๋ฉฐ ์คํฌ๋ฆฝํธ ํ๊ทธ๊ฐ ์์ต๋๋ค. script.js๋ผ๊ณ ํฉ์๋ค. ๊ฐ๋ฐ์์๋ ๋ค๋ฅธ Grunt ์์ ์ ์คํํฉ๋๋ค. ์ด ์์ ์ ์บ์ฑ์ ๋ฐฉ์งํ๊ธฐ ์ํด ํ์์คํฌํ์ ํจ๊ป ๊ฐ๋ณ์ ์ผ๋ก ๋น๋๊ธฐ์์ผ๋ก ๋๋จธ์ง JS ํ์ผ์ ๋ก๋ํ๋ ๋ฐ ํ์ํ ํ์ผ๋ง ์ฐ๊ฒฐํฉ๋๋ค. ์ด ์ฐ๊ฒฐ๋ ์คํฌ๋ฆฝํธ ์ธํธ๋ ๋ด๊ฐ ๊ฐ๋ฐํ๋ ๋์ script.js๋ฅผ ๋์ฒดํฉ๋๋ค. ๋ ์๋๋ฆฌ์ค ๋ชจ๋ index.html์์ ์คํฌ๋ฆฝํธ ํ๊ทธ์ ํจ๊ป script.js๋ง ๋ก๋ํฉ๋๋ค.
๋ฐ๋ผ์ ๊ฐ๋ฐ์์ ์๋ ๋ถํธ์คํธ๋ฉ์ ์ํํด์ผ ํฉ๋๋ค. ํ๋ก๋์ JS์ ์ฐ๊ฒฐํ์ง ์์ ๋จ์ผ ํ์ผ์ ํฌํจํ ์ ์๊ธฐ ๋๋ฌธ์ ์ฝ์ต๋๋ค. ๋ค๋ฅธ ๋ชจ๋ JS๊ฐ ๋ก๋๋ ํ์ ์ด ์คํฌ๋ฆฝํธ๋ฅผ ๋ก๋ํด์ผ ํฉ๋๋ค.
bootstrap_dev.js
.element(document.body)
.ready(function() {
angular.bootstrap(document.body, ['App']);
});
ํ๋ก๋์ ์์๋ ๊ฐ๋๊ธฐ๊ฐ ๋ง์์ ๋ค์ง ์๊ธฐ ๋๋ฌธ์ ์๋์ผ๋ก ๋ถํธ์คํธ๋ฉํ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ด ํ๋ก๋์ ์ฝ๋์ ๋ํด ๊ฐ๋๊ธฐ๋ฅผ ์คํํ๊ณ ์์ต๋๋ค. ๋ํ HTML์์ "ng-app" ์์ฑ์ ์ ๊ฑฐํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ด Development์์ ์กฐ๊ธฐ์ ๋ถํธ์คํธ๋ฉ๋์ง ์๋๋ก ํ์ฌ ์ผ๋ฐ์ ์ธ ์๋ ๋ถํธ์คํธ๋ฉ์ด ๋ฐ์ํ์ง ์์ต๋๋ค. ์ ๋ script.js์์ ๋ค๋ฅธ ๋ชจ๋ JS๋ณด๋ค ์คํฌ๋ฆฝํธ๋ฅผ ๋จผ์ ๋ฐฐ์นํ์ฌ ํ๋ก๋์ ์์ ์ด๋ฅผ ๋ณด์ํฉ๋๋ค.
bootstrap_prod.js
window.document.body.setAttribute("ng-app", "myApp");
"myApp"์ ๊ธฐ๋ณธ ๋ชจ๋์ ์ด๋ฆ์ผ๋ก ๋ฐ๊พธ์ญ์์ค.
๊ฐ๋ฐ์์ ์์ ์์ ์ ์ํํ๋ฉด ์คํฌ๋ฆฝํธ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๋ก๋ํ๊ณ ์๋ ๋ถํธ์คํธ๋ฉ์ ์ํํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ํ๋ก๋์ ์ฝ๋์ ๋ํด์๋ง ๊ฐ๋๊ธฐ ํ ์คํธ๋ฅผ ์คํํ๋ ๊ฒ์ด ๊ด์ฐฎ๋ค๋ฉด(์คํฌ๋ฆฝํธ ํ๊ทธ๋ฅผ ํตํด ๋จ์ผ ํ์ผ์์ ๋ก๋ํ ์ ์์ต๋๋ค. Angular๊ฐ ๋ก๋๋๊ธฐ ์ ์ ng-app ์ง์๋ฌธ์ ์ถ๊ฐํ๋ฉด ์๋ ๋ถํธ์คํธ๋ฉ์ด ์ํ๋ฉ๋๋ค. ์ด์ ๊ฐ๋ฐ์์ ๋ค์ํ ์คํฌ๋ฆฝํธ๋ฅผ ๋น๋๊ธฐ์์ผ๋ก ๋ก๋ํ๊ณ ํ๋ก๋์ ์ฝ๋์ ๋ํด ๊ฐ๋๊ธฐ ํ ์คํธ๋ฅผ ์คํํฉ๋๋ค.
๋๋ฅผ ์ํด ์ผํ๊ฒ ๋ ๊ฒ์ ๋ด protractor.conf.js์ ๋ค์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ๊ฒ์ด์์ต๋๋ค.
onPrepare: function()
{
// implicit and page load timeouts
browser.manage().timeouts().pageLoadTimeout(40000);
browser.manage().timeouts().implicitlyWait(25000);
// for non-angular page
browser.ignoreSynchronization = true;
// sign in before all tests
}
๋ ์ข์ ๋ฐฉ๋ฒ์ด ์๋ค๋ฉด ์๋ ค์ฃผ์ธ์ =]
๋น์ทํ ๋ฌธ์ ๊ฐ์์์ต๋๋ค. ์ด ์ ์ฉํ ์ ๋ณด๋ฅผ ์ฐพ์์ต๋๋ค.
์ฑ์ด ํ๋ก๋์ ์์ ๋ถ์คํธ๋ฉ๋๋ ๋ฐฉ์์๋ ์ฐจ์ด๊ฐ ์์ง๋ง ํ ์คํธ์์ ๊ฐ๋๊ธฐ๋ ๋ค์๊ณผ ๊ฐ์ด ์ํํฉ๋๋ค.
๋ฐ๋ผ์ ํ๋ก๋์ ์ฝ๋๊ฐ ์ด ํ๋ก์ธ์ค์ ์ถฉ๋ํ์ง ์๋์ง ํ์ธํ์ญ์์ค. angular๋ ํ ๋ฒ๋ง ๋ถ์คํธ๋ฉ๋ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค. angular.boostrap() ๋๋ angular.resumeBoostrap()์ ๋ํ ๋ ๋ฒ์งธ ํธ์ถ์ ๊ฐ๋๊ธฐ ๋๋ ์ฑ์ ์์์ํฌ ๊ฒ์ ๋๋ค. ๋ฐ๋ผ์ ๋ณธ๋ฌธ์ ng-app ์ด ์ ํ ํ์ํ์ง ์์ผ๋ฉฐ angular.boostrap()์ ์ง์ ํธ์ถํ ์ ์์ผ๋ฉฐ NG_DEFER_BOOTSTRAP์ ์ฌ์ฉํ์ฌ ๊ฐ๋๊ธฐ์ ์ํด ์ฒ๋ฆฌ๋ฉ๋๋ค! ์ ๋๋ก.
๋ก๋ฉ ๋ฐ ๋ถํ ์ค์ window.name ์ด ์ฑ ์ฝ๋์ ์ํด ๋ณ๊ฒฝ๋์ง ์์๋์ง ํ์ธํ์ญ์์ค. ์ ๊ฒฝ์ฐ์๋ jquery ๊ฐ ๊ทธ๋ ๊ฒํ๊ณ ์์๋๋ฐ ์ด์ ๋ฅผ ์ ์ ์์ต๋๋ค. ๋ด ํ๋ก์ ํธ์์ jquery ๋ฅผ ์์ ํ ์ ๊ฑฐํ๊ณ ํ ์คํธ๊ฐ ์๋ํ์ต๋๋ค. ์ด๊ฒ์ด ๋ฌธ์ ์ ๊ทผ์์ด๋ค!
์ง๊ธ์ ๋ด ํ๋ก์ ํธ์ ํ์ํ๊ธฐ ๋๋ฌธ์ ํ ์คํธ๋ฅผ ์ค๋จํ์ง ์๊ณ jquery๋ฅผ ๋ค์ ๊ฐ์ ธ์ค๋ ๋ฐฉ๋ฒ์ ์ฐพ๊ณ ์์ต๋๋ค!
๋๋์ด,
์ฐ๋ฆฌ์ ๋ฌธ์ ๋ ํ ํ์ด์ง์ ์๋ ๋ ๊ฐ์ง ๋ฒ์ ์ jQuery์ ์์์ต๋๋ค(๋ ๋ฒ์งธ ๋ฒ์ ์ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ํด ๋ก๋๋จ). ๊ทธ๊ฒ์ ์ ๊ฑฐํ๊ณ ํ์ด์ง์ jQuery๋ง ๋จ๊ฒจ๋๋ฉด ๋ชจ๋ ๊ฒ์ด ์๋ํฉ๋๋ค.
์ผ๋ถ ์ฝ๋๊ฐ ๋์์ด ๋ ์ ์์ต๋๋ค.
index.html
<body>
...
<script data-main="require.conf" type="text/javascript" src="bower_components/requirejs/require.js"></script>
</body>
requirejs ๊ตฌ์ฑ
...
deps: ["proto/main"],
...
ํ๋กํ .js
// We just load deps and register the module:
define(["deps"],function(){
angular.module('proto'...
}
ํ๋กํ /๋ฉ์ธ.js
define(["require", "exports", "proto", "angular"],
function (require, exports) {
angular.bootstrap(document, ['proto']);
});
๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก ํ ์คํธ ์ฌ์
var protractor = require('protractor');
var ptor;
function prepareBrowser() {
ptor = protractor.getInstance();
return ptor.get(ptor["baseUrl"] + '/login').then(function () {
console.log("got");
return ptor.waitForAngular().then(function () {
console.log("ngwaited");
});
});
}
describe('container', function () {
beforeEach(function () {
return prepareBrowser();
});
describe('form tests', function () {
it('form is shown', function () {
expect($('[ng-controller=LoginContr]').isDisplayed()).toBeTruthy();
});
});
});
์ฐ์ถ
Using the selenium server at http://localhost:4444/wd/hub
got
ngwaited
container
form tests
form is shown - pass
Finished in 3.112 seconds
1 test, 1 assertion, 0 failures
๋ง์ธ!
ng-app
์์ angular.bootstrap()
๋ก ์ ํํ์ ๋ projotractor.conf ์ rootElement: 'div'
๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ๊ฐ๋๊ธฐ๋ฅผ ๋ค์ ์๋์ํค๋ ๋ฐ ํ์ํ ์ ์ผํ ๋ณ๊ฒฝ ์ฌํญ์ด์์ต๋๋ค. ์ฐ๋ฆฌ๋ ๊ฐ๋ 1.3.0๊ณผ ๊ฐ๋๊ธฐ 1.3.1์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
@jongunnip ๋ต ์ ๋ ์ ๋ดค์ต๋๋ค ๊ฐ์ฌํฉ๋๋ค!
@mikaraento ๋๋ถ์ ์ ๋์์ด์~
์๋ ํ์ธ์ ์ฌ๋ฌ๋ถ, ์ด ์ค๋ ๋์ ๊ด๋ จ๋ ์ด์ํ ๊ฒ์ ๋๋ค.
๋๋ ์ผ๋ฐ ๊ฐ๋ ์ฑ์ ๊ฐ์ง๊ณ ์์ต๋๋ค. requirejs๋ ์๊ณ , ๊ฐ๋๊ธฐ 2.0.0์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
์คํ
์ด์ง ํ๊ฒฝ์์ ์์คํ
ํ
์คํธ๋ฅผ ์คํํ๋ฉด ์๋ํฉ๋๋ค.
๋ก์ปฌ์์ ์คํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ํ์๋ฉ๋๋ค.
org.openqa.selenium.WebDriverException: unknown error: [ng:btstrpd] App Already Bootstrapped with this Element
... ๋ฉ์์ง
grunt:server
๋์ ์ ์ ๋ฆฌ์์ค๋ฅผ ์ ๊ณตํ๋๋ก Nginx ์๋ฒ๋ฅผ ๊ตฌ์ฑํ๋ฉด ๋ชจ๋ ๊ฒ์ด ์๋ํฉ๋๋ค.
๊ฐ๋ฐ ์ค์ livereload๋ก ์คํํด๋ ์๊ด์๋์?
์ ์ ์๋ฒ๊ฐ ๋ค๋ฅด๊ฒ ์๋ํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ด๊ณ grunt๋ ์ผ๋ถ 404 ๋๋ ๋ฆฌ๋๋ ์ ์ ์ ๊ณตํ๋ฏ๋ก ์น ๋๋ผ์ด๋ฒ๊ฐ ๋ฏธ์ณ๊ฐ๋๋ค.
์๋ ํ์ธ์ ์ฌ๋ฌ๋ถ,
๋ง์ ์๊ฐ์ ์ฝ๊ณ ์ฝ์ ํ ๋ด ํ๋ก์ ํธ์ ์ ๋ง๋ ํ๋์ ์๋ฃจ์ ์ ์ฐพ์์ต๋๋ค( ๊ฐ๋๊ธฐ ์คํ ํ๋ฆ ์์ ).
?protractor-test
์ถ๊ฐ( ํ๋ก์ ํธ ์ฝ๋ )if(location.href.indexOf("protractor-test") < 0){
// start angular app
angular.bootstrap(document, [module.name]);
} else {
// start angular app to protractor tests
window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
angular.bootstrap(document, [module.name]);
}
๊ทธ๋ฆฌ๊ณ ๋ง์นจ๋ด ๋ชจ๋ ๋ค์ ์๋ํฉ๋๋ค \o/
@juliemr ์ฌ๋ณธ๊ณผ ํจ๊ป
์ ๋ ์ต๊ทผ(๋จ 1-2์ฃผ) ๊ฐ๋๊ธฐ๋ฅผ ์ฌ์ฉํ์ฌ Angular ์์ฉ ํ๋ก๊ทธ๋จ์ ํ ์คํธํ๊ธฐ ์์ํ์ต๋๋ค. ๋ด ์์ฉ ํ๋ก๊ทธ๋จ์๋ ๋ก๊ทธ์ธ ํ์ด์ง์ ๋ณธ๋ฌธ ํ๊ทธ์ ng-App์ด ์ ์๋์ด ์์ต๋๋ค. ๋ด ํ ์คํธ๋ "Angular๋ฅผ ํ์ด์ง httP://(WebSite Address)์์ ์ฐพ์ ์ ์์ต๋๋ค. ๋ฌธ์ ๊ฐ ํ์คํ์ง ์์ ๊ฒฝ์ฐ(๋ธ๋ผ์ฐ์ ignoresync ๋งค๊ฐ๋ณ์๋ฅผ ์ผ๋ฉด ์ ๋๋ก ์๋ํ์ง๋ง ์ด๊ฒ์ ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ์ด ์๋๋๋ค), Juliemr์ด ์ ์ํ ๊ฒ์ ์๋ํ ์ ์์ง๋ง(๋ ๊ฑฐ์ ๋๋ผ์ด๋ฒ๋ฅผ ์ฌ์ฉํ์ฌ ํ์), ๋ด ์ฑ์ด ๋ถํํ๋ ์ด์ ๋ฅผ ์ดํดํ๋ ค๊ณ ํฉ๋๋ค. body ํ๊ทธ์ ng-App์ด ์์ด๋. ์ฑ ๊ธฐ๋ฅ ๋์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.. ๋ก๊ทธ์ธ ํ์ด์ง.. ์ผ๋จ ๋ก๊ทธ์ธํ๋ฉด ํ ํ์ด์ง์ ์ผ๋ถ ๋งํฌ๋ฅผ ํด๋ฆญํ๋ฉด ๋ค๋ฅธ ์ฐฝ/ํญ์ด ์ด๋ฆฝ๋๋ค. ๋ถํธ์คํธ๋ฉ์ด ์๋์ผ๋ก ์ํ๋๋์ง ํ์ธํ๋ ๋ฐ ํ์ํ ๋ชจ๋ ๋จ๊ณ๋ฅผ ์ฐพ๋ ๋ฐ ๋์์ด ๋ ๊น์? bootstrap.js ํ์ผ์์ ๋ฌด์ธ๊ฐ๋ฅผ ํ์ธํด์ผ ํฉ๋๊น(bootstrap ํ์ผ์ ng-app ๋งค๊ฐ๋ณ์๊ฐ ํ์๋์ง ์์ ํ์ธํฉ๋๋ค. ๊ฐ์ฌํฉ๋๋ค.
์ด ๋ฌธ์ ์ ๋ํ ์ ๋ฐ์ดํธ๊ฐ ์์ต๋๊น? ng-app์ด ์๋ ๊ฐ๋ ์ฑ์์ ์๋ํ๋๋ก ๊ฐ๋๊ธฐ๋ฅผ ๊ตฌ์ฑํ๋ ๋ชจ๋ฒ ์ฌ๋ก ๋๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ ๋๊น? @erkobridee ๊ทํ์ ์๋ฅผ ๋ ์์ธํ ์ค๋ช ํด ์ฃผ์๊ฒ ์ต๋๊น? ๊ทธ๋ฆฌ๊ณ ๊ฐ๋๊ธฐ๊ฐ ๋ก๋๊ฐ ์๋ฃ๋ ๋ ๊ฐ๋๊ธฐ๊ฐ ๊ธฐ๋ค๋ ค์ผ ํ๋ ๋ฐฉ๋ฒ๊ณผ ๊ด๋ จํ์ฌ window.name์ ์ฌ๋ฐ๋ฅด๊ฒ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํ ์ ์์ต๋๊น?
@juliemr Angular 1.5์์๋ ์๋์ผ๋ก ๋ถํธ์คํธ๋ฉํ๋ ๊ฒ์ด ๋ถํธ์คํธ๋ฉ์ ์ ํธ๋๋ ๋ฐฉ๋ฒ์ ๋๋ค. ๋์ค์ ์ถ๋ณต๋ฐ์ ๊ฐ๋๊ธฐ ์ง์์ด ์์ต๋๊น?
+1 ์ด ๋ฌธ์ ๊ฐ ๋ ๋์ ์ฐ์ ์์๋ฅผ ๊ฐ์ ธ์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค. ๊ฐ๋๊ธฐ๊ฐ ์๋ ๋ถํธ์คํธ๋ฉ๊ณผ ํจ๊ป ์๋ํ์ง ์์ผ๋ฉด ๊ฐ๋๊ธฐ๋ฅผ ์ฌ์ฉํ๋ ์ด์ ์ด ์์ต๋๋ค. Webdriver๋ฅผ ํตํด ๊ฐ๋๊ธฐ๋ฅผ ์ฌ์ฉํ๋ ์์ ์ ๊ฐ๋๊ธฐ๊ฐ ๋ชจ๋ ๊ฐ๋ ๋๊ธฐํ๋ฅผ ์ฒ๋ฆฌํ๋ค๋ ๊ฒ์ ๋๋ค.
๊ฐ์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ๋ชจ๋ ์ ๋ฐ์ดํธ?
์๋ ํ์ธ์, ์ด๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํ ๊ณํ์ด๋ ํ๋ก์ธ์ค๊ฐ ์์ต๋๊น?
์ด์ํ๊ฒ ํผ๋๋ฐฑ์ด ์์ต๋๋ค. ng-app์ด ๋๋ฝ๋ ์ฑ์ด ์๊ณ ํ
์คํธํ ์ ์์ต๋๋ค.
๊ฐ๋๊ธฐ๋ก ๊ทธ๋ค์
2016๋ 7์ 16์ผ ์ค์ 5์ 1๋ถ์ "millerwx" ์๋ฆผ @github.com์ด ์์ฑํ์ต๋๋ค.
์๋ ํ์ธ์, ์ด๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํ ๊ณํ์ด๋ ํ๋ก์ธ์ค๊ฐ ์์ต๋๊น?
โ
๋น์ ์ด ๋๊ธ์ ๋ฌ์๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๋ฐ๋ ๊ฒ์ ๋๋ค.
์ด ์ด๋ฉ์ผ์ ์ง์ ๋ต์ฅํ๊ณ GitHub์์ ํ์ธํ์ธ์.
https://github.com/angular/protractor/issues/66#issuecomment -233104701,
๋๋ ์ค๋ ๋ ์์๊ฑฐ
https://github.com/notifications/unsubscribe-auth/AOghFs2ff0e-88DLafNGD3iCH8uCOgVaks5qWEmIgaJpZM4A82d7
.
2016๋ ํ์ฌ๊น์ง ์ด๊ฒ์ ์ฌ์ ํ โโ๋์๊ฒ ๋ฌธ์ ์์ต๋๋ค. ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Magnetized ๋ต๋ณ์ ๋ฐ๋์ต๋๋ค. ๋๋ ๋จ์ง ์ฝ๊ฐ์ ๋ณํ๋ฅผ ์ฃผ์๋ค. ๋ํ์์ ํ๋ฅญํ๊ณ ๋ฒ์๋ฅผ ๋ฒ์ด๋ ๊ฒ์ ๋ฆฌ๋๋ ์ ์ ์ถ์ ํ๊ณ ์ฑ ๋ด๋ถ๋ก ๋ค์ ์ด๋ํ๋ ๊ฒ์ ๋๋ค. *์ ๋ Babel๋ก ํ๋๋ฐ ES5๋ก ์ฐ์ ๋ ๋ฉ๋๋ค.
`describe('๋ก๊ทธ์ธ ํ๋ฉด', ()=>{
var urlTimeout = 9000;
const waitForUrlRegex = (regex, urlMatcher )=>{
return driver.wait(()=>{
return driver.getCurrentUrl().then( (url)=>{
return regex.test(url);
});
}, urlTimeout, "Expectation error: Timeout for url not matching " + urlMatcher );
};
const waitForRoute = (route)=>{
return waitForUrlRegex( new RegExp( route + "$" ), route );
}
const waitForUrl = (url)=>{
return waitForUrlRegex( new RegExp( "^https?:\/\/" + url ), url );
}
it('should go to homepage and click login button', ()=>{
driver.get( app_url, urlTimeout);
//there is a redirect to welcome route
expect( waitForRoute("welcome") ).toBe(true);
const loginButton = element.all(by.className("login_button")).get(0);
loginButton.click();
});
it( 'should enter credentials', ()=>{
expect( waitForUrl( non_angular_login_url ) ).toBe(true);
driver.findElement( by.name("user")).sendKeys( "username");
driver.findElement( by.name("password")).sendKeys( "userpassword");
driver.findElement( by.tagName("button")).click();
});
it( 'should go back to app, and continue testing', ()=>{
expect( waitForRoute( "user-screen") ).toBe(true);
});
});`
config.on์ค๋น
onPrepare: ()=>{
require("babel-register");
global.driver = browser.driver;
}
๋ฆฌ๋๋ ์ ์ ์กฐ์ฌํ๊ณ ์ถ์ง ์์ ๊ฒฝ์ฐ ๋๋ผ์ด๋ฒ๋ฅผ ์ ์๊ธฐ ์ํ๋ก ๋ ๋ค์ ๊ฐ๋๊ธฐ๋ก ์์ ์ ์์ํ๋ฉด ๋ฉ๋๋ค.
`driver.get( app_url+ "/#/ํ์", urlTimeout);
driver.sleep( 1000 );
const loginButton = element.all(by.className("login_button")).get(0);
loginButton.click();`
์ฐ๋ฆฌ๋ ์ค์ ๋ก ์๋ ๋ถํธ์คํธ๋ฉ์ ์ง์ํฉ๋๋ค. ์ด ์ ๋ณด๋ ์ค๋๋์์ต๋๋ค. https://github.com/angular/protractor/issues/3857 ๋ ์ฒ๋ฆฌํ๋ฉด ๋ถํธ์คํธ๋ํ์ด ํจ์ฌ ๋ ์์ ์ ์ด ๋ฉ๋๋ค.
@sjelin ์๋ ๋ถํธ์คํธ๋ฉ์ ์ง์ํ๋ ๋ฌธ์ํ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ ๋๊น? ๊ฐ๋๊ธฐ 5.0์ ๋ํ ๊ด๋ จ ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
์๋ ๋ถํธ์คํธ๋ํ ์ค์ ๋์ผํ ๋ฌธ์ ์ ์ง๋ฉดํ์ต๋๋ค. grunt์ ํจ๊ป ์คํํ ์๋ฃจ์
์ ์ฐพ์์ต๋๋ค.
๊ฐ๋๊ธฐ ๋ฒ์ 2.5.1์ ์ฌ์ฉํ์ต๋๋ค.
grunt.initConfig({
protractor: {
options: {
configFile: "protractor.conf.js", // Default protractor config file
keepAlive: true, // If false, the grunt process stops when the test fails.
noColor: false, // If true, protractor will not use colors in its output.
args: {
// Arguments passed to the command
}
},
e2e: {
options: {
// Stops Grunt process if a test fails
keepAlive: true
}
}
}
})
npm install grunt-protractor-runner --save-dev
์ค์น ๋ฐ ๋ก๋ ์์
grunt.loadNpmTasks('grunt-protractor-runner')
์ถ๊ฐ
๋ค์ ๋ช
๋ น์ผ๋ก ๊ฐ๋๊ธฐ๋ฅผ ์คํํ์ญ์์ค.
grunt protractor
๋๋ ์ ํ๊ตฐ ์ด๋ฆ์ผ๋ก
grunt protractor --suite suite_name
๊ทธ๊ฒ์ด ๋น์ ์ ์ํด ์๋ํ๋์ง ์๋ ค์ฃผ์ญ์์ค.
์ด๊ฒ์ ๋๋ฅผ ์ํด ์ผํ์ต๋๋ค.
๋ธ๋ผ์ฐ์ .ignoreSynchronization = true;
๋ธ๋ผ์ฐ์ .get(URL);
๋ธ๋ผ์ฐ์ .ignoreSynchronization = ๊ฑฐ์ง;
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
@sjelin ์๋ ๋ถํธ์คํธ๋ฉ์ ์ง์ํ๋ ๋ฌธ์ํ๋ ๋ฐฉ๋ฒ์ ๋ฌด์์ ๋๊น? ๊ฐ๋๊ธฐ 5.0์ ๋ํ ๊ด๋ จ ์ ๋ณด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.