Marcel Birkner
Bastian Krol
Big Data Nerds
Agile Ninjas
Continuous Delivery Gurus
Java Specialists
Performance Geeks
Hipster Developers
And we are looking for more!
This is frontend only
Ideal: backend & frontend in one CD pipeline
Grunt — Task Runner
SASS — CSS preprocessor
compass — images → base 64 data URIs
concat: {
options: {
separator: '\n;'
},
app: {
src: [
'<%= jsSrcDir %>/js/**/*.js',
],
dest: '<%= jsTargetDir %>/app.js'
}
}
uglify: {
app: {
files: {
'<%= jsTargetDir %>/app.min.js': [ '<%= jsTargetDir %>/app.js' ],
}
}
}
.img-foo {
background-image: inline-image("foo.png");
}
sass: {
app: {
options: { compass: true, },
files: { '<%= cssTargetDir %>/master.css': '<%= cssSrcDir %>/master.scss', }
}
},
.img-foo {
background-image:
url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAA...');
}
versioning: {
options: {
grepFiles: [ '<%= appTargetDir %>/**/*.html', ]
},
css: {
src: [
'<%= cssTargetDir %>/app.min.css',
]
},
js: {
src: [
'<%= jsTargetDir %>/app.min.js',
'<%= jsTargetDir %>/vendor.min.js',
]
},
},
<!DOCTYPE html>
<html lang="en">
<head>
...
<link href="css/app.min.css" rel="stylesheet">
...
</head>
<body>
...
<script src="js/vendor.min.js" type="text/javascript"></script>
<script src="js/app.min.js" type="text/javascript"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
...
<link href="css/app.min.b678e30139fc04.css" rel="stylesheet">
...
</head>
<body>
...
<script src="js/vendor.min.dda09628f6e1da.js" type="text/javascript"></script>
<script src="js/app.min.8e46534a4f66158.js" type="text/javascript"></script>
</body>
</html>
| Production | Development | |
| JS | concatenated, minified | source files, not minified |
| CSS | compiled (SASS), concatenated, minified | only compiled (SASS) |
| Images | Embedded into CSS | Embedded into CSS (by Sass/Compass) |
| HTML | references optimized assets | references source assets |
<!DOCTYPE html>
<html lang="en">
<head>
...
<!-- build:css css/app.min.css -->
<link rel="stylesheet" href="css/master.css">
<link rel="stylesheet" href="css/dashboard.css">
...
<!-- /build -->
</head>
<body>
<!-- build:js js/app.min.js -->
<script src="js/app.js" type="text/javascript"></script>
<script src="js/routes.js" type="text/javascript"></script>
...
<!-- /build -->
</body>
</html>
processhtml: {
dist: {
files: {
'<%= appTargetDir %>/index.html': ['<%= appSrcDir %>/index.html']
}
}
},
Alternative: grunt-usemin to concat, minify & replace in one step
watch: {
files: [
'<%= jsSrcDir %>/**/*.js',
'<%= cssSrcDir %>/**/*.scss',
'<%= htmlSrcDir %>/**/*.html',
],
tasks: [
'dev-build',
],
options: {
livereload: true,
}
}
grunt.registerTask('dev-build', [
'copy:cssThirdParty',
'sass',
]);
var expect = chai.expect;
beforeEach(module('project-staffing'));
describe('UpperCase Test', function() {
it('should convert first charactor to UpperCase', inject(function(uppercaseFilter) {
expect(uppercaseFilter('a')).to.equal('A');
expect(uppercaseFilter('hello world')).to.equal('Hello World');
}));
});
npm install -g karma-cli karma start karma.conf.js // or grunt karma
var ActivityService;
var $http;
beforeEach(inject(function(_ActivityService_, _$http_) {
ActivityService = _ActivityService_;
$http = _$http_;
sinon.stub($http, 'post', function(){});
}));
describe('Activity Service', function() {
it('should have send http POST to backend after saving one activity',
inject(function(ActivityService) {
ActivityService.saveActivity('user', 'action', 'object');
expect($http.post.callCount).to.equal(1);
}));
});
Should
chai.should();
foo.should.be.a('string');
foo.should.equal('bar');
Expect
var expect = chai.expect;
expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
Assert
var assert = chai.assert; assert.typeOf(foo, 'string'); assert.equal(foo, 'bar');
describe('Manage customer', function() {
var ptor;
beforeEach(function() {
browser.get('/');
ptor = protractor.getInstance();
element(by.id('navEmployees')).click();
element(by.id('navListEmployees')).click();
});
it('should navigate to list employees page', function() {
expect(ptor.getCurrentUrl()).toMatch(/#\/list-employees/);
});
});
it('should find employee Maria on list search page', function() {
createMultipleEmployees(); // Creates employees: Max, Maria, Daniel, John
...
element(by.id('searchText')).sendKeys('Ma');
expect(element.all(by.id('employee')).count()).toBe(2);
element(by.id('searchText')).sendKeys('ria');
expect(element.all(by.id('employee')).count()).toBe(1);
});
npm install -g protractor webdriver-manager start protractor test/client/e2e/conf.js
http://54.75.209.193/jenkins/view/EnterJS-Pipeline/
Info: Server will be shutdown after talk
project-staffing git:(master) ✗ docker-compose
Commands:
build Build or rebuild services
help Get help on a command
kill Kill containers
logs View output from containers
port Print the public port for a port binding
ps List containers
pull Pulls service images
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
restart Restart services
up Create and start containers
➜ project-staffing git:(master) ✗ docker-compose up
Recreating projectstaffing_mongodb_1...
Creating projectstaffing_nodejsserver_1...
Building nodejsserver...
Step 0 : FROM tcnksm/centos-ruby
---> 255207061af8
Step 1 : RUN yum install -y npm
---> Using cache
---> c8ca0ad1bec0
Step 2 : COPY . /opt/project-staffing/
---> dc70b159f357
...
Step 5 : CMD node /opt/project-staffing/server.js
---> Running in 78d831b9f0f0
---> 88b07ba248a0
Successfully built 88b07ba248a0
...
Updated Slides
Project Documentation
Source Code @ Github
Do you have comments or questions?