├── .gitignore
├── .istanbul.yml
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── app.js
├── ideas-for-tips.md
├── lib
├── checker.js
├── checks
│ ├── compatibility
│ │ ├── css-prefixes.js
│ │ └── flash-detection.js
│ ├── integration
│ │ ├── manifest.js
│ │ └── web-manifest.json
│ ├── interactions
│ │ ├── alert.js
│ │ └── touchscreen-target.js
│ ├── performance
│ │ ├── compression.js
│ │ ├── exif.js
│ │ ├── http-errors.js
│ │ ├── number-requests.js
│ │ └── redirects.js
│ ├── responsive
│ │ ├── doc-width.js
│ │ ├── fonts-size.js
│ │ ├── meta-viewport.js
│ │ └── screenshot.js
│ └── utils.js
├── css-prefixes.json
├── ejs-filters.js
├── issues-scale.json
├── logs
│ └── logs.ejs
├── profiles
│ ├── default.json
│ ├── smartphone2.json
│ └── tablet.json
├── reports
│ ├── compatibility
│ │ ├── css-prefixes
│ │ │ ├── mismatching-prefixes.ejs
│ │ │ └── missing-prefixes.ejs
│ │ └── flash-detection
│ │ │ ├── swf-file-detected.ejs
│ │ │ └── swfobject-lib-detected.ejs
│ ├── format.js
│ ├── integration
│ │ └── manifest
│ │ │ ├── httperror.ejs
│ │ │ ├── httperror_property.ejs
│ │ │ ├── jsonerror.ejs
│ │ │ ├── jsonserror.ejs
│ │ │ ├── multiple-manifests.ejs
│ │ │ └── networkerror_property.ejs
│ ├── interactions
│ │ ├── alert
│ │ │ └── alert-detected.ejs
│ │ └── touchscreen-target
│ │ │ └── too-small-touchscreen-target.ejs
│ ├── performance
│ │ ├── compression
│ │ │ └── resources-could-be-compressed.ejs
│ │ ├── exif
│ │ │ └── images-could-be-unexified.ejs
│ │ ├── http-errors
│ │ │ ├── favicon.ejs
│ │ │ └── http-errors-detected.ejs
│ │ ├── number-requests
│ │ │ └── info-number-requests.ejs
│ │ └── redirects
│ │ │ └── redirects-encountered.ejs
│ └── responsive
│ │ ├── doc-width
│ │ └── doc-width-too-large.ejs
│ │ ├── fonts-size
│ │ └── too-small-font-size.ejs
│ │ └── meta-viewport
│ │ ├── content-viewport-missed.ejs
│ │ ├── hardcoded-viewport-width.ejs
│ │ ├── invalid-viewport-value.ejs
│ │ ├── no-viewport-declared.ejs
│ │ ├── non-standard-viewport-parameter-declared.ejs
│ │ ├── several-viewports-declared.ejs
│ │ ├── unknow-viewport-parameter-declared.ejs
│ │ └── users-are-prevented-to-zoom.ejs
└── tips
│ ├── accessible.html
│ ├── appmanifest.html
│ ├── deviceapis.html
│ ├── dontpushaway.html
│ ├── offline.html
│ ├── pushnotifications.html
│ ├── responsiveimages.html
│ └── serviceworker.html
├── package.json
├── public
├── css
│ ├── bootstrap-sortable.css
│ ├── bootstrap-theme.min.css
│ ├── bootstrap.min.css
│ ├── logs.css
│ ├── report.css
│ └── style.css
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
├── img
│ ├── cog.svg
│ ├── developers-min.svg
│ ├── favicon.ico
│ ├── heart.svg
│ ├── mobile-checker-icon.png
│ ├── mobile-checker-icon.svg
│ ├── mobile-checker.svg
│ ├── mobilechecker-logo-w3c.png
│ ├── mobilechecker-logo-w3c.svg
│ ├── mobilechecker-logo.png
│ ├── opensource.svg
│ ├── opensource_logo.png
│ ├── smartphone.svg
│ ├── smartphone2.svg
│ ├── tablet.svg
│ ├── w3c-developers-test-3.svg
│ ├── w3c-developers.svg
│ └── w3c.svg
├── index.html
├── js
│ ├── bootstrap-sortable.js
│ ├── bootstrap.min.js
│ ├── jquery.min.js
│ ├── logs.js
│ ├── script.js
│ └── socket.io.js
├── logs.ejs
├── manifest.json
└── octicons
│ ├── LICENSE.txt
│ ├── README.md
│ ├── octicons-local.ttf
│ ├── octicons.css
│ ├── octicons.eot
│ ├── octicons.less
│ ├── octicons.svg
│ ├── octicons.ttf
│ ├── octicons.woff
│ └── sprockets-octicons.scss
├── test
├── mocha.opts
├── test_server
│ ├── public
│ │ ├── css
│ │ │ ├── bootstrap-theme.min.css
│ │ │ ├── bootstrap.min.css
│ │ │ └── style.css
│ │ ├── docs
│ │ │ ├── 404-manifest.html
│ │ │ ├── 404-manifest.json
│ │ │ ├── badjson-manifest.html
│ │ │ ├── badmanifest.json
│ │ │ ├── brokenlinks-manifest.html
│ │ │ ├── brokenlinks-manifest.json
│ │ │ ├── brokenlinks-manifest2.html
│ │ │ ├── brokenlinks-manifest2.json
│ │ │ ├── compressed.html
│ │ │ ├── css-inconsistent-prefixes.html
│ │ │ ├── css-prefixes.html
│ │ │ ├── firework.jpg
│ │ │ ├── font-size-ok.html
│ │ │ ├── http-errors-favicon.html
│ │ │ ├── http-errors.html
│ │ │ ├── icon1.png
│ │ │ ├── icon2.svg
│ │ │ ├── inconsistent-prefixes.css
│ │ │ ├── invalid-manifest.html
│ │ │ ├── invalidmanifest.json
│ │ │ ├── manifest.json
│ │ │ ├── multiple-manifests.html
│ │ │ ├── no-manifest.html
│ │ │ ├── number-requests.html
│ │ │ ├── prefixes.css
│ │ │ ├── redirects.html
│ │ │ ├── too-small-font-size.html
│ │ │ ├── too-small-touchscreen-target.html
│ │ │ ├── touchscreen-target-ok.html
│ │ │ ├── uncompressed.html
│ │ │ ├── viewport_incorrect-initial-scale.html
│ │ │ ├── viewport_incorrect-width.html
│ │ │ ├── viewport_many-viewport.html
│ │ │ ├── viewport_no-initial-scale.html
│ │ │ ├── viewport_no-meta-viewport.html
│ │ │ ├── viewport_no-width.html
│ │ │ ├── viewport_ok.html
│ │ │ ├── width_fail.html
│ │ │ └── width_success.html
│ │ ├── fonts
│ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ ├── glyphicons-halflings-regular.svg
│ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ └── glyphicons-halflings-regular.woff
│ │ ├── index.html
│ │ └── js
│ │ │ ├── bootstrap.min.js
│ │ │ ├── jquery.min.js
│ │ │ └── script.js
│ └── test_app.js
└── tests.js
└── tools
└── getListOfCssPrefixEquivalents.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Coverage directory used by tools like istanbul
6 | coverage
7 |
8 | public/*.png
9 | node_modules
10 |
--------------------------------------------------------------------------------
/.istanbul.yml:
--------------------------------------------------------------------------------
1 | instrumentation:
2 | include-all-sources: true
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | dist: trusty
2 | language: node_js
3 | node_js:
4 | - "4"
5 | addons:
6 | apt:
7 | sources:
8 | - google-chrome
9 | packages:
10 | - google-chrome-stable
11 | install:
12 | - echo "Starting install phase..."
13 | - wget https://github.com/lightbody/browsermob-proxy/releases/download/browsermob-proxy-2.1.4/browsermob-proxy-2.1.4-bin.zip
14 | - unzip browsermob-proxy-2.1.4-bin.zip
15 | - ln -s browsermob-proxy-2.1.4 browsermob-proxy-2.0-beta-9
16 | - browsermob-proxy-2.1.4/bin/browsermob-proxy --use-littleproxy false &
17 | - npm install
18 | - echo "Install phase completed."
19 | - google-chrome --version
20 | before_script:
21 | - export DISPLAY=:99.0
22 | - sh -e /etc/init.d/xvfb start
23 | script:
24 | - echo "Running tests..."
25 | - mocha --timeout 20000 --globals Intl,IntlPolyfill,ErrorHandler test/tests.js
26 | - echo "Tests completed."
27 | after_script:
28 | - echo "Running coveralls..."
29 | - npm run coveralls
30 | - echo "Running coveralls completed."
31 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Mobile Checker
2 |
3 | Looking to contribute to Mobile Checker? There is many ways to get involved in its developement.
4 | Please take few minutes to read this document to understand how you can help in Mobile Checker developement.
5 |
6 | ## Add a new check
7 |
8 | It is pretty simple to add a new check to the mobile checker. Create a new check require only an experience in a client side JavaScript developement.
9 |
10 | A new check is composed by 2 composants:
11 | * the script itself, who will be executed on the tested web page.
12 | * the issue template, sent to the user.
13 | * testing.
14 |
15 | ### script
16 | All check scripts are located in the [lib/checks](https://github.com/w3c/Mobile-Checker/tree/master/lib/checks) directory. You just have to add your own script file in the correct category. Of course, feel free to create a new category if no category match with your check. Ready? Let's create your first check!
17 |
18 | This is the basic template of check file. Save it in the lib directory:
19 |
20 | ````javascript
21 | var self = this;
22 | exports.name = "name-of-the-check"; //write here the name of your check. Have to match with the file's name.
23 | exports.category = "name-of-the-category"; //write here the name. Have to match with a category's directory name.
24 | exports.check = function(checker, browser) {
25 | /*
26 | * here your code.
27 | */
28 | };
29 | ```
30 |
31 | The ```check(checker, browser)``` function, receive ```checker``` and ```browser``` as parameters.
32 | * Use ```browser``` to test the web page.
33 | * Use ```checker``` to report the result.
34 |
35 | N.B: The ```browser``` object is based on the [mobile-web-browser-emulator API](https://github.com/w3c/mobile-web-browser-emulator) which is based on [selenium WebDriverJS API](https://code.google.com/p/selenium/wiki/WebDriverJs). That allow you to use all the functions provided by these APIs.
36 |
37 |
38 | #### access to mobile-web-browser-emulator API:
39 | ````javascript
40 | exports.check = function(checker, browser) {
41 | // get a browser object which can be use with mobile-web-browser-emulator API
42 | };
43 | ```
44 | example:
45 | ````javascript
46 | exports.check = function(checker, browser) {
47 | browser.takeScreenshot(__dirname + "/../screenshot.png"); //mobile-web-browser-emulator method
48 | };
49 | ```
50 |
51 | #### access to selenium WebDriverJS API:
52 | ````javascript
53 | browser.do(function(driver) { //take a function in parameter
54 | // get a driver object which can be use with selenium webdriver
55 | });
56 | ```
57 | example:
58 | ````javascript
59 | exports.check = function(checker, browser) {
60 | browser.do(function(driver) {
61 | driver.getTitle().then(function(title) { //selenium webdriver method
62 | console.log(title);
63 | });
64 | });
65 | };
66 | ```
67 | N.B: WebDriverJS uses "promises" to track the state of each operation. [learn more](https://code.google.com/p/selenium/wiki/WebDriverJs#Promises).
68 |
69 | #### run your own script:
70 | ````javascript
71 | exports.check = function(checker, browser) {
72 | browser.do(function(driver) {
73 | return driver.executeScript(function() {
74 | //write your own script and return what you need.
75 | });
76 | });
77 | };
78 | ```
79 |
80 | #### report a result:
81 | to report a result, use the report function of the checker object.
82 | ````javascript
83 | checker.report(key, name, category, status, data)
84 | ```
85 | * **key**: name of the file issue to display in the report
86 | * **name**: self.name
87 | * **category**: self.category
88 | * **status**: error, warning or info
89 | * **data**: data to display in the issue reported (optional)
90 |
91 |
92 | ### issue template
93 | All check issues are located in the [lib/issues](https://github.com/w3c/Mobile-Checker/tree/master/lib/issues) directory. You just have to add your own issue file in the correct path as ```category/name-of-your-check/your-file```. You can create all issues you need. That allow you to use more than once the ```checker.report(key, name, category, status, data)``` function in your script.
94 |
95 | The Mobile Checker use [EJS](http://www.embeddedjs.com/) to write the issues sent to the client side. That allow you to insert data in the reported issue.
96 |
97 | template:
98 | ````html
99 |
100 |
101 |
102 |
103 |
104 | <%=category%>
105 |
106 |
107 |
Fix it:
108 |
109 |
110 | ```
111 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 World Wide Web Consortium
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://travis-ci.org/w3c/Mobile-Checker)
4 | [](https://coveralls.io/r/w3c/Mobile-Checker)
5 | [](https://david-dm.org/w3c/Mobile-Checker)
6 | [](https://david-dm.org/w3c/Mobile-Checker#info=devDependencies)
7 | [](https://gitter.im/w3c/Mobile-Checker)
8 |
9 | The Mobile Checker is a tool for Web developers who want to make their Web page or Web app work better on mobile devices.
10 |
11 | The Mobile Checker was built to provide all of us web developers with a new and helpful experience of mobile Web developement.
12 | We built the base. Now join us, and make it grant your wishes. We hope you will make it awesome.
13 |
14 | ## How does it work?
15 | This tool is a full JavaScript Web application built with [Node.js](http://nodejs.org/) and [Selenium WebDriver](http://docs.seleniumhq.org/projects/webdriver/). Based on the [mobile Web browser emulator API](https://github.com/w3c/mobile-web-browser-emulator), the Mobile Checker combines powerful technologies to simulate a Web browser on a mobile device.
16 | That's why, contrary to most of the current online mobile emulators, the Mobile Checker can provide an emulation close to what your Web app looks like on different kinds of mobile devices, including tablets and smartphones.
17 |
18 |
19 | ## Installation
20 | Mobile Checker is a Node application. It will eventually be distributed through npm, but in the meantime
21 | you can simply clone this repository:
22 |
23 | git clone https://github.com/w3c/Mobile-Checker.git
24 |
25 | 1. Install [Node.js](http://nodejs.org/)
26 |
27 | 2. Install npm dependencies:
28 |
29 | ```
30 | npm install -d
31 | ```
32 |
33 | 3. In addition to the npm dependencies, install:
34 |
35 | * [Google Chrome](https://www.google.com/chrome/)
36 | * [browsermob-proxy](https://github.com/lightbody/browsermob-proxy/) running on port 8080
37 | * [ImageMagick](http://www.imagemagick.org/)
38 | * [XVFB](http://www.x.org/archive/X11R7.6/doc/man/man1/Xvfb.1.xhtml)
39 |
40 | ## Running
41 | In your terminal, run:
42 |
43 | node app.js
44 |
45 | Then, connect on the localhost:3000 port.
46 |
47 | ## Testing
48 | Testing is done using mocha. Simply run:
49 |
50 | mocha --timeout 30000
51 |
52 | from the root and you will be running the test suite. Mocha can be installed with:
53 |
54 | npm install -g mocha
55 |
56 | ## Feedback and contributions
57 |
58 | * **Send feedback** about the tool and join us on the [mailing list](public-qa-dev@w3.org).
59 | * **Discuss** about the Mobile Checker in our [Gitter room](https://gitter.im/w3c/Mobile-Checker).
60 | * **Report a bug** and open an issue on [Github](https://github.com/w3c/Mobile-Checker/issues).
61 | * **Send a feature request** on [Github](https://github.com/w3c/Mobile-Checker/issues).
62 | * **Contribute** to the Mobile Checker by first reading the [contribution guidelines](https://github.com/w3c/Mobile-Checker/blob/master/CONTRIBUTING.md).
63 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | global.rootRequire = function(name) {
2 | return require(__dirname + '/' + name);
3 | };
4 |
5 |
6 | var express = require("express"),
7 | app = express(),
8 | http = require('http').Server(app),
9 | compress = require('compression'),
10 | io = require('socket.io')(http),
11 | util = require("util"),
12 | Checker = require("./lib/checker").Checker,
13 | events = require("events"),
14 | uuid = require('node-uuid'),
15 | mkdirp = require('mkdirp'),
16 | ejs = require('ejs'),
17 | path = require('path'),
18 | fs = require("fs"),
19 | insafe = require("insafe"),
20 | formatter = require('./lib/reports/format.js');
21 |
22 | var SCREENSHOTS_DIR = 'public/tmp/screenshots/';
23 |
24 | var checklist = [
25 | require('./lib/checks/performance/number-requests'), require(
26 | './lib/checks/performance/redirects'), require(
27 | './lib/checks/performance/http-errors'), require(
28 | './lib/checks/performance/compression'), require(
29 | './lib/checks/responsive/doc-width'), require(
30 | './lib/checks/responsive/meta-viewport'), require(
31 | './lib/checks/responsive/screenshot'), require(
32 | './lib/checks/compatibility/flash-detection'), require(
33 | './lib/checks/compatibility/css-prefixes'), require(
34 | './lib/checks/interactions/alert'), require(
35 | './lib/checks/integration/manifest')
36 | ];
37 |
38 | var logs = {
39 | currentState: {
40 | clients: 0,
41 | validations: 0
42 | },
43 | history: {
44 | startDate: new Date(),
45 | clients: 0,
46 | validations: 0
47 | }
48 | };
49 |
50 | /*
51 | * Job Manager
52 | * Manage a job queue to avoid a server overload
53 | */
54 | var maxJobs = 15;
55 | var jobQueue = [];
56 | var currentJobs = 0;
57 |
58 | function newJob(checker, options) {
59 | if (currentJobs < maxJobs) {
60 | currentJobs++;
61 | checker.check(options);
62 | } else {
63 | addJobToQueue(checker, options);
64 | }
65 | }
66 |
67 | function addJobToQueue(checker, options) {
68 | jobQueue.push({
69 | checker: checker,
70 | options: options
71 | });
72 | options.events.emit('wait');
73 | }
74 |
75 | function removeJobToQueue(jobIndex){
76 | jobQueue.splice(jobIndex, 1);
77 | }
78 |
79 | function runNewJobFromQueue() {
80 | if (currentJobs < maxJobs) {
81 | currentJobs++;
82 | jobQueue[0].options.events.emit('jobStarted');
83 | jobQueue[0].checker.check(jobQueue[0].options);
84 | removeJobToQueue(0);
85 | } else {
86 | return;
87 | }
88 | }
89 |
90 | function endJob(){
91 | if (currentJobs > 0)
92 | currentJobs--;
93 | if(jobQueue.length > 0)
94 | runNewJobFromQueue();
95 | }
96 |
97 | function removeDisconnectJobToQueue(checkId) {
98 | for(var i = 0; i < jobQueue.length - 1; i++) {
99 | if(jobQueue[i].options.id == checkId) {
100 | removeJobToQueue(i);
101 | }
102 | }
103 | }
104 |
105 | function init() {
106 | createFolder(SCREENSHOTS_DIR, clearScreenshotFolder);
107 | }
108 |
109 | function updateLogs(code, socket) {
110 | switch (code) {
111 | case 'NEW_CLIENT':
112 | logs.currentState.clients++;
113 | logs.history.clients++;
114 | break;
115 | case 'NEW_VALIDATION':
116 | logs.currentState.validations++;
117 | logs.history.validations++;
118 | break;
119 | case 'CLIENT_DECONNECTED':
120 | logs.currentState.clients--;
121 | break;
122 | case 'VALIDATION_ENDED':
123 | logs.currentState.validations--;
124 | break;
125 | }
126 | socket.broadcast.emit('logs', reportLogs());
127 | }
128 |
129 | function reportLogs() {
130 | var str = fs.readFileSync(path.join(__dirname, '/lib/logs/logs.ejs'), 'utf8');
131 | return ejs.render(str, logs);
132 | }
133 |
134 | function createFolder(path, cb) {
135 | mkdirp(path, function(err) {
136 | if (err) console.error(err);
137 | else cb();
138 | });
139 | }
140 |
141 | function unlinkFile(path) {
142 | fs.unlink(path, function(err) {
143 | if (err) throw err;
144 | });
145 | }
146 |
147 | function unlinkScreenshot(filename) {
148 | unlinkFile(SCREENSHOTS_DIR + filename);
149 | }
150 |
151 | var clearScreenshotFolder = function() {
152 | fs.readdir(SCREENSHOTS_DIR, function(err, files) {
153 | files.forEach(function(name) {
154 | unlinkScreenshot(name);
155 | });
156 | });
157 | };
158 |
159 | function displayTip(socket) {
160 | setTimeout(function() {
161 | fs.readdir("lib/tips", function(err, files) {
162 | var tip = "lib/tips/" + files[Math.floor(files.length * Math.random())];
163 | fs.readFile(tip, {
164 | encoding: "utf-8"
165 | }, function(err, data) {
166 | if (err) {
167 | return;
168 | }
169 | formatter.format("
Some of the CSS style sheets use prefixed properties, i.e. properties that may have not been standardized yet and that are specific to a given browser.
6 |
While using prefixed properties make it possible for a page to use leading edge advances in CSS, it is important that they be used in a way that does not affect compatibility with other browsers. Some prefixed properties have inconsistent values across prefixed names.
Some of the CSS style sheets use prefixed properties, i.e. properties that may have not been standardized yet and that are specific to a given browser.
6 |
While using prefixed properties make it possible for a page to use leading edge advances in CSS, it is important that they be used in a way that does not affect compatibility with other browsers. Some prefixed properties are missing their equivalent for other browsers.
7 |
8 | <% var urls = Object.keys(missingPrefixes); %>
9 | <% for(var i=0; i < urls.length; i++) {%>
10 |
Make sure that the manifest file is well-formed JSON; watch out in particular that JSON only accepts double-quotes ("), that these are required around property names, and JSON does not accept trailing commas.
It thus risks not being interpreted correctly by browsers.
10 |
11 |
12 |
Make sure that the manifest file uses the properties is well-formed JSON; watch out in particular that JSON only accepts double-quotes ("), that these are required around property names, and JSON does not accept trailing commas.
<%= size.width.length %> activable targets have been detected that are smaller than 48px wide or 48px high.
6 |
7 |
Code
Tag
Size
8 |
9 | <%for(var index in size.width){%>
10 |
11 |
<%=outerHtml[index]%>
<%=tag[index]%>
<%=size.width[index]%>x<%=size.height[index]%>
12 | <%}%>
13 |
14 |
15 |
Most mobile devices are prominently touch-based, which means they need larger activable targets than mouse-based devices. It is recommended that activable targets (such as links and form fields) be at least 48x48 pixels large to make them easy to hit with a finger.
16 |
17 |
18 |
19 | Style activable targets so that their width and height are larger than 48px.
20 |
<%=number%> images displayed on the page could be transmitted more quickly (saving <%= Math.floor(saving / 1024) %> kB) if their embedded metadata were removed:
The page does not have a favicon at <%=url%> but the browser had to make a request to determine it.
6 |
Favicon helps users quickly identify a particular page in a list of opened windows; unless told otherwise, browsers always make requests to /favicon.ico to obtain these favicons. When no favicon is present, this generates unnecessary network traffic.
7 |
8 |
9 |
10 |
Set up a favicon on the domain, or specific to the page; otherwise, use <link rel='icon' href='data:;base64,iVBORw0KGgo='> in the head of the page to prevent the browser from doing the additional request. See Understanding the Favicon for in-depth information on browser and platform support.
You can reduce the number of network requests by combining (concatenating) files such as JavaScript and CSS files. It's also possible to combine several image files into a single larger image and use CSS to display parts of that image as needed — a technique known as CSS sprites. See The Mystery Of CSS Sprites for a detailed explanation and many useful links.
The width of the page is larger than an average mobile browser size
3 |
The width of the page is larger than an average mobile browser size - this means users will have to scroll horizontally and vertically, or zoom in and out.
<%=content[index]%> (<%=tag[index]%> element) display with small font size. (<%=size[index]%> pixels CSS)
9 | <%}%>
10 |
11 |
On a mobile device, it's recommended to not put font-size lower than 16px because the user experience will be deteriorated with small font size. It could force user to zoom on their mobile device.
12 |
13 |
14 |
15 |
It's recommended to put font-size greater than 16px.
The meta viewport uses an hardcoded value for the width of the viewport.
4 |
A numeric hardcoded value for the meta viewport declaration makes the page less responsive to the actual screen used; if the width is larger than the screen width, the page will be hard to read and will not benefit from some optimizations (e.g. removal of the 300 ms delay in clicks); if the page is significantly more narrow than the screen (as is likely on tablets), the page will not benefit from the whole screen real estate.
5 |
6 |
7 |
Use device-width as the value of the width parameter in the meta viewport declaration. Learn more about the mobile viewport
No mobile viewport declared. The meta viewport tag helps adapt the way Web pages are displayed in mobile browsers.
4 |
Without meta viewport declared, mobile browsers will render a Web page as they would on a ~1000px screen; in most cases, this will force users to zoom before they can interact with the page, and then they will have to scroll awkwardly to navigate on the page.
5 |
6 |
7 |
Declare a mobile viewport using a meta viewport tag in the head of the HTML markup. Learn more about the mobile viewport
The following properties are used in a meta viewport declaration but are not defined in the reference specification:
6 | <% for(var i=0; i < nonstandardProperties.length; i++) {%>
7 | <%= nonstandardProperties[i] %>
8 | <% if (i < nonstandardProperties.length - 1) {%>, <% } %>
9 | <% } %>
10 |
11 |
Relying on their usage may create inconsistent user experiences across browsers.
12 |
13 |
14 |
15 |
Avoid using non-standard properties in the meta viewport declaration. Learn more about the mobile viewport.
More than one viewports are declared in the HTML. Please check your HTML file keep a single meta viewport
6 |
7 |
Only the last meta viewport declaration is taken into account; the other declarations should be removed as they clutter the code and may have an impact on performance.
8 |
9 |
10 |
11 |
Keep only the last meta viewport declaration. Learn more about the mobile viewport.
The following properties are used in a meta viewport declaration but cannot be recognized as valid values:
6 | <% for(var i=0; i < unrecognizedProperties.length; i++) {%>
7 | <%= unrecognizedProperties[i] %>
8 | <% if (i < unrecognizedProperties.length - 1) {%>, <% } %>
9 | <% } %>
10 |
11 |
They may be due to a coding error, or linked to proprietary extensions of the meta viewport.
12 |
13 |
14 |
15 |
Check there is no type in the meta viewport declaration and avoid using non-standard properties in the meta viewport declaration. Learn more about the mobile viewport.
The meta viewport declaration prevents users to zoom in on the page.
6 |
Except in some specific cases (e.g. games), preventing users to zoom in a page is discouraged; users may want to see details in an image, or may have trouble reading at the current zoom level.
7 |
8 |
9 |
10 |
Remove the constraints on user-scalable or minimum-/maximum-scale. Learn more about the mobile viewport.
The W3C Web Accessibility Initiative has started documenting techniques to make sure that your Web app works well with assistive technologies on mobile devices: make sure to check them out to ensure the broadest possible adoption of your app!
The Manifest for Web applications specification defines a JSON format that lets you describe your Web app with a name, a preferred start page, icons, and a specific display mode (e.g. fullscreen).
3 |
Mobile browsers are starting to use these manifests to facilitate the addition of Web apps to users’ home screens, a great way to keep your users just one-click away from your app!
Think you need a native app to interact with mobile devices specific hardware? Think again!
3 |
Mobile browsers already let you access the GPS, accelerometer, compass, camera, microphone and battery gauge of the device via JavaScript APIs, and work on adding support for more sensors is under way in W3C — you can stay abreast of all the activity in this space via the quarterly updated overview of standards for Web apps on mobile.
Once users have landed on to your mobile Web app, don’t start by pushing them away to a native app; if your native app provides additional features, mention that to the user once they got a chance to try and find what they wanted on the Web app first!
Mobile devices are great because they’re always connected… except when they’re not, or when the connection is poor.
3 |
To keep your users engaged even in adverse networking conditions, look into making your Web app work offline: the widely deployed HTML5 application cache and its more experimental but much better replacement ServiceWorker let you make your Web app run seamlessly even in the deepest tunnels.
Mobile Web apps can now start benefitting from push notifications, making it possible for your users to subscribe to the latest news and events from your Web app, even after they closed their browsers.
3 |
Well-designed push notifications have been shown to significantly grow user’s engagement, so give them a try!
If you’re using responsive design for your mobile Web app, you may want to take advantage of the ability to declare your images at various resolution (both in markup and style sheets), so that mobile browsers will load the most adapted image given their screen resolution.
3 |
The <picture> element event lets you declare separate images based on more advanced media queries.
ServiceWorker is a technology under active development in W3C and under implementation in various browsers; it promises a very flexible framework to handle offline Web applications, and in the longer term, a model to have Web apps work in the background, even with no active browser.
3 |
Check out the specification, and if you feel like it, come and join us in evaluating it by building example apps.
'
203 | + ''
204 | + ' error while resolving '
205 | + data
206 | + '. Check the spelling of the host, the protocol (http, https) and ensure that the page is accessible from the public Internet. '
207 | + '
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "W3C Mobile Checker",
3 | "lang": "en",
4 | "start_url": "./",
5 | "icons": [
6 | {"src": "img/mobile-checker-icon.png",
7 | "sizes": "any",
8 | "type": "image/png"
9 | }
10 | ],
11 | "display": "standalone"
12 | }
13 |
--------------------------------------------------------------------------------
/public/octicons/LICENSE.txt:
--------------------------------------------------------------------------------
1 | (c) 2012-2015 GitHub
2 |
3 | When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos)
4 |
5 | Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL)
6 | Applies to all font files
7 |
8 | Code License: MIT (http://choosealicense.com/licenses/mit/)
9 | Applies to all other files
10 |
--------------------------------------------------------------------------------
/public/octicons/README.md:
--------------------------------------------------------------------------------
1 | If you intend to install Octicons locally, install `octicons-local.ttf`. It should appear as “github-octicons” in your font list. It is specially designed not to conflict with GitHub's web fonts.
2 |
--------------------------------------------------------------------------------
/public/octicons/octicons-local.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w3c/Mobile-Checker/a5f9691f6ff7b29be5d86bea9d11be50954615a4/public/octicons/octicons-local.ttf
--------------------------------------------------------------------------------
/public/octicons/octicons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w3c/Mobile-Checker/a5f9691f6ff7b29be5d86bea9d11be50954615a4/public/octicons/octicons.eot
--------------------------------------------------------------------------------
/public/octicons/octicons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w3c/Mobile-Checker/a5f9691f6ff7b29be5d86bea9d11be50954615a4/public/octicons/octicons.ttf
--------------------------------------------------------------------------------
/public/octicons/octicons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/w3c/Mobile-Checker/a5f9691f6ff7b29be5d86bea9d11be50954615a4/public/octicons/octicons.woff
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --timeout 20000
2 |
--------------------------------------------------------------------------------
/test/test_server/public/css/style.css:
--------------------------------------------------------------------------------
1 | ul {
2 | list-style-type: none;
3 | }
4 | a {
5 | padding-left: 20px;
6 | color: #E54E27;
7 | }
8 | span {
9 | color: #E54E27;
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/test/test_server/public/docs/404-manifest.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Manifest is 404
5 |
6 |
7 | Manifest is 404
8 |
--------------------------------------------------------------------------------
/test/test_server/public/docs/404-manifest.json:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Manifest is 404
5 |
6 |
7 | Manifest is 404
8 |
--------------------------------------------------------------------------------
/test/test_server/public/docs/badjson-manifest.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Manifest is broken JSON
5 |
6 |
7 | Manifest is bad JSON
8 |
--------------------------------------------------------------------------------
/test/test_server/public/docs/badmanifest.json:
--------------------------------------------------------------------------------
1 | {
2 |
--------------------------------------------------------------------------------
/test/test_server/public/docs/brokenlinks-manifest.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Manifest with broken links
5 |
6 |
7 | Manifest with broken links
8 |
--------------------------------------------------------------------------------
/test/test_server/public/docs/brokenlinks-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "start_url": "/url/does/not/exist"
3 | }
4 |
--------------------------------------------------------------------------------
/test/test_server/public/docs/brokenlinks-manifest2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Manifest with broken links
5 |
6 |
7 | Manifest with broken links
8 |
--------------------------------------------------------------------------------
/test/test_server/public/docs/brokenlinks-manifest2.json:
--------------------------------------------------------------------------------
1 | {
2 | "icons": [{"src": "//domain-does-not-exist"}]
3 | }
4 |
--------------------------------------------------------------------------------
/test/test_server/public/docs/compressed.html:
--------------------------------------------------------------------------------
1 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
2 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
3 |
4 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
5 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
6 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
7 |
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
3 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum
4 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum