Web
A WALKTHROUGH
@nikjohn
WHAT'S THE POINT?
-
WEB TECHNOLOGY STACK
-
WEB DEVELOPMENT PROCESS
-
WEB TEAM
-
Q & A
CONTENTS
-
SIZES
-
PAYLOADS
-
ARCHITECTURE
-
FRAMEWORKS
-
TESTING
-
TRANSLATION
-
DEPLOYMENT
SIZES
- Adaptive application - pages are served by device specs.
- WURFL - a 3rd party User Agent-based device detection service
4 SIZES
- txt
- xs
- sm
- lg
txt
width < 128px
OR
(ua.indexOf('/midp') > -1)
xs
Wireless Device
AND
(Proxied Browser
OR
width < 320px
OR
Pointing method != touch)
sm
Wireless Device
AND
!XS
lg
!Wireless Device
AND
!Legacy (e.g. IE8)
Sizes | Resolution | Wireless Device |
Special Condition |
---|---|---|---|
txt |
< 128 px |
Yes |
- ua contains ‘midp’ |
xs |
< 320 px |
Yes |
- Non-touch device - Proxy browser |
sm |
Yes |
- Not ‘xs’ |
|
lg |
No |
- Not legacy (e.g. IE8) |
PAYLOAD
- With the exception of some interactive content, everything is rendered server-side.
- txt has, unlike other platforms, very few exceptions and mostly gets whatever is not exclusive to any other size
Markup/Styling
- XS/SM/LG share a lot of markup and styling, SM/XS intersects the most.
- Utilities exist to deliver markup/styling to each platform in a structured manner.
mixin character-count(max)
if LG || SM
p.char-counter(data-max-chars=max)
span.current
| 0
span.divider
|/
span.max
= max
xs |
---|
sm |
---|
lg |
---|
.only-xs() |
.only-sm() |
.only-lg() |
.until-lg() |
.from-sm() |
STYLES
.item-detail.is-job {
.item-intro {
text-align: left;
}
.until-lg({
.item-intro {
padding-bottom: 1em;
border-bottom: solid 1px @color-linnen-grey;
}
});
}
STYLES
- Most pages have a Backbone view, which is optional
- Handles all post-render DOM interaction for Javascript capable clients.
JAVASCRIPT
WHAT IS SENT ON THE WIRE?
ARCHITECTURE
SIMPLIFIED
BROWSER
MIDDLEWARE (NODE JS + EXPRESS JS)
API
http(s)
http(s)
BROWSER
MIDDLEWARE (NODE JS + EXPRESS JS)
API
http://bikroy.com/EN/ads/ads-in-bangladesh
GET http://api.bikroy.com/v1/serp
GET http://bikroy.com/EN/ads/ads-in-bangladesh
WHY A MIDDLEWARE?
-
SEO *
-
FEATURE PHONES
*Google supports crawling of JS-based applications, but recommends progressive enhancement still.
FRAMEWORKS
&
LIBRARIES
txt |
---|
xs |
---|
sm |
---|
lg |
---|
NODE JS (4.x)
NODE JS (4.x)
EXPRESS JS (4.x)
BACKBONE JS MODELS
PUG (JADE) TEMPLATES
BACKBONE VIEWS *
ZEPTO JS
* Admin uses a slimmer PoC as well - Furiosa
* No dependency in any direction between Backbone views and models
SERVER
CLIENT
TESTING
- Unit testing for server libs, middlewares and helpers
- Functional testing for anything that is part of the UI
- Visual regression testing during CI builds to check UI integrity
Unit testing
- Minimal unit testing is performed on independent components
- Primary tool is mocha
var helpers = require('../../helpers/unit'),
images = require(helpers.base + 'server/middleware/images');
suite('server/middleware/images', function() {
test('images sm, jpg', function(done) {
var request = {
},
response = {
locals: {
platform: 'lg'
}
};
images(request, response, function() {
assert.equal(response.locals.sizes.full.length, 2);
assert.ok(response.locals.sizes.full[0].indexOf('.jpg') > -1,
'image size has .jpg format');
done();
});
});
test('images sm, webp', function(done) {
var request = {
headers: {
accept: 'text/html;q=0.9,image/webp,*/*;q=0.8'
}
},
response = {
locals: {
platform: 'sm'
}
};
images(request, response, function() {
assert.equal(response.locals.sizes['list-normal'].length, 2);
assert.ok(response.locals.sizes['list-normal'][0].indexOf('.webp') > -1,
'image size has .webp format');
done();
});
});
});
- Performed with Selenium/Phantom.js, Nightwatch.js, Saucelabs (optionally), and a bunch of scaffolding of our own in the web-tester project.
- Represents the clear majority of testing of the web application. Runs for all flows (not all permutations of all flows).
FUNCTIONAL TESTING
- Uses mock end points generated by the api-functional-mock project
- Can be run on Selenium or PhantomJS (Headless)
FUNCTIONAL TESTING
-
Loads a page for a certain market and locale, then runs commands to manipulate and assert application state. (~4k assertions in entire suite)
FUNCTIONAL TESTING
var helpers = require('../../helpers/functional');
module.exports = helpers.runAllSites({
landing: function(market, locale) {
var test = function(client) {
var locales = helpers.markets[market].locales,
countrySlug = helpers.markets[market].countrySlug,
countryName = helpers.t('site.country', market, locale),
expected;
client.start('', market, locale)
.shootAll(2)
.assert.gtm({
platform: 'lg'
});
if (locales.length > 1) {
if (market === 'bikroy') {
expected = (locale === 'en' ? 'bn' : 'en');
}
else if (market === 'ikman') {
expected = (locale === 'en' ? 'si' : 'en');
}
client
.assert.elementPresent('.ui-nav .locales')
.assert.elementPresent('.locales li:nth-child(5n+' + (locales.length - 1) + ')')
.getAttribute('.locales li:nth-child(5n+1) a', 'href', function(result) {
this.assert.ok(result.value.indexOf('/' + expected) > -1,
'Locale link URL');
});
}
else {
client.assert.elementNotPresent('.ui-nav .locales');
}
if (locale === helpers.markets[market].localeDefault) {
client
.assert.attributeMatches('link[rel="canonical"]', 'href',
new RegExp('http:\/\/' + market + '\\.test(:\\d+)?\/' +
locale))
.assert.attributeMatches('.ui-logo a', 'href', '/$');
}
else {
client
.assert.attributeMatches('link[rel="canonical"]', 'href',
new RegExp('http:\/\/' + market + '\\.test(:\\d+)?\/' +
locale))
.assert.attributeMatches('.ui-logo a', 'href', '/' + locale + '$');
}
client
.assert.elementPresent('link[rel="prefetch"]')
.assert.elementPresent('link[rel="prerender"]')
.assert.translation('.home-top h1', 'home.top.title',
market, locale, {
country: countryName
})
.assert.elementsCount('.home-locations .home-group', 2)
.assert.elementsCount('.home-locations .home-group.is-city ul', 2)
.assert.elementsCount('.home-locations .home-group.is-region ul', 1)
.assert.elementPresent('.home-focus')
.assert.elementPresent('.home-safety')
.assert.elementPresent('.home-fb')
.assert.elementPresent('.home-categories')
.assert.elementsCount('.home-categories .col-12', 7)
.assert.attributeMatches('.home-categories .col-12:first-child a', 'href',
'/ads/food-agriculture-in-' + countrySlug + '-47')
.assert.containsText('.home-categories .col-12:first-child .count', '54,234');
if (helpers.markets[market].news) {
client
.assert.elementPresent('.home-news')
.assert.elementsCount('.home-news li', 2);
}
client.end();
};
test.apiMock = 'web/home/landing';
return test;
}
});
TO BE CONTINUED..
Saltside Web Stack
By Nikhil John
Saltside Web Stack
- 728