@tgwf/co2 0.5.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/unittests.yml +26 -0
- package/CHANGELOG.md +52 -2
- package/README.md +39 -10
- package/data/Lean-ICT-Materials-1byte-Model-2018.xlsx +0 -0
- package/data/Lean-ICT-Materials-Forecast-Model-2018.xlsx +0 -0
- package/data/fixtures/url2green.test.db +0 -0
- package/data/fixtures/url2green.test.json +1 -0
- package/data/fixtures/url2green.test.json.gz +0 -0
- package/package.json +18 -11
- package/src/1byte.js +14 -5
- package/src/1byte.test.js +16 -0
- package/src/co2.js +5 -5
- package/src/co2.test.js +126 -118
- package/src/green-byte.js +1 -1
- package/src/{hostingAPI.js → hosting-api.js} +6 -8
- package/src/hosting-api.test.js +37 -0
- package/src/hosting-database.test.js +31 -0
- package/src/hosting-json.js +75 -0
- package/src/hosting-json.test.js +44 -0
- package/src/hosting.js +12 -11
- package/src/hosting.test.js +31 -41
- package/src/index.js +1 -1
- package/.travis.yml +0 -11
- package/src/hostingAPI.test.js +0 -18
- package/src/hostingDatabase.js +0 -80
- package/src/hostingDatabase.test.js +0 -25
- package/url2green.test.db +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Unit tests
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- main
|
|
6
|
+
pull_request:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
node-version: [12.x, 14.x, 16.x]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v2
|
|
17
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
18
|
+
uses: actions/setup-node@v1
|
|
19
|
+
with:
|
|
20
|
+
node-version: ${{ matrix.node-version }}
|
|
21
|
+
- name: Install
|
|
22
|
+
run: npm ci
|
|
23
|
+
- name: Verify lint
|
|
24
|
+
run: npm run lint
|
|
25
|
+
- name: Run unit tests
|
|
26
|
+
run: npm test
|
package/CHANGELOG.md
CHANGED
|
@@ -4,10 +4,60 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
-
|
|
7
8
|
|
|
8
|
-
##
|
|
9
|
+
## Unreleased
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
- Include a new alternative, "Green Byte Model" with the figures after speaking to folks at the IEA and other places.
|
|
12
|
+
|
|
13
|
+
# [0.8.0] - 2021-11-28
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Update further dependencies
|
|
18
|
+
- Fix embarassing order of magnitude typo in 1byte model (thanks @mstaschik!)
|
|
19
|
+
|
|
20
|
+
## Added
|
|
21
|
+
|
|
22
|
+
- Read JSON blob also as gzipped #44 (thanks @soulgalore)
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- The 1bye model will give different numbers now. It's mentioned in `#fixed` but it's worth repeating.
|
|
27
|
+
|
|
28
|
+
## [0.7.0] - 2021-11-28
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Update tests to avoid network requests #50
|
|
33
|
+
- Update dependencies across the board
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Switch to github actions instead of travis for CI.
|
|
38
|
+
|
|
39
|
+
## [0.6.1] - 2020-03-15
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Added the function to load JSON, on the tgwg.hosting object, for use in the sustaiable web sitespeed plugin.
|
|
44
|
+
|
|
45
|
+
## [0.6.0] - 2020-03-15
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- Added the hosting-JSON for running local checks against an array instead of SQLite.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- Swapped out checking against a sqlite database `hosting-json`in favour of simple array in,
|
|
54
|
+
- Updated conventions for style - using kebab-cases over CamelCase for naming files
|
|
55
|
+
|
|
56
|
+
### Removed
|
|
57
|
+
|
|
58
|
+
- Extracted sqlite usage and dependencies into a separate module, `url2green`. This means you no longer need to compile SQLite on install.
|
|
59
|
+
|
|
60
|
+
## [0.5.0] - 2020-03-03
|
|
11
61
|
|
|
12
62
|
### Changed
|
|
13
63
|
|
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# CO2
|
|
2
2
|
|
|
3
|
-
<img src="https://
|
|
3
|
+
<img src="https://github.com/thegreenwebfoundation/co2.js/actions/workflows/unittests.yml/badge.svg" />
|
|
4
|
+
|
|
4
5
|
|
|
5
6
|
We know computers use electricity, and because most of the electricity we use comes from burning fossil fuels to generate, there is an environmental cost to every upload and download we make over the internet.
|
|
6
7
|
|
|
7
|
-
We
|
|
8
|
+
We can do something about this though. The same way we use performance budgets to make apps and websites faster and cheaper to run, we can use carbon budgets to make them faster, cheaper and _greener_.
|
|
8
9
|
|
|
9
10
|
The CO2 package from [The Green Web Foundation][tgwf] lets you quickly estimate these emissions, to make measurable improvements as part of your workflow.
|
|
10
11
|
|
|
@@ -24,17 +25,40 @@ This is open source software, with all the guarantees associated, so if you want
|
|
|
24
25
|
|
|
25
26
|
## Usage
|
|
26
27
|
|
|
28
|
+
### Calculating emissions per byte
|
|
29
|
+
|
|
30
|
+
#### Server-side
|
|
31
|
+
|
|
32
|
+
This approach relies on the `fs` module and so can only be used on platforms, like Node.js, that support this.
|
|
33
|
+
|
|
27
34
|
```js
|
|
28
35
|
|
|
29
36
|
const CO2 = require('@tgwf/co2')
|
|
30
|
-
const bytesSent =
|
|
37
|
+
const bytesSent = (1024 * 1024 * 1024)
|
|
31
38
|
const co2Emission = new CO2();
|
|
32
39
|
estimatedCO2 = co2Emission.perByte(bytesSent)
|
|
33
40
|
|
|
34
|
-
console.log(`Sending a
|
|
41
|
+
console.log(`Sending a gigabyte, had a carbon footprint of ${estimatedCO2.toFixed(3)} grams of CO2`)
|
|
35
42
|
|
|
36
43
|
```
|
|
37
44
|
|
|
45
|
+
#### Browser-side
|
|
46
|
+
|
|
47
|
+
For browser-based solutions, you must import the `co2.js` submodule directly from `node_modules`. For example, like this:
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
|
|
51
|
+
const CO2 = require('node_modules/@tgwf/co2/src/co2.js')
|
|
52
|
+
const bytesSent = (1024 * 1024 * 1024)
|
|
53
|
+
const co2Emission = new CO2();
|
|
54
|
+
estimatedCO2 = co2Emission.perByte(bytesSent)
|
|
55
|
+
|
|
56
|
+
console.log(`Sending a gigabyte, had a carbon footprint of ${estimatedCO2.toFixed(3)} grams of CO2`)
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Checking for green power
|
|
61
|
+
|
|
38
62
|
Because different digital services and websites use different forms of power, there is also a module for checking if a domain uses green power or not, and whether the domains linked to on a page use green power as well.
|
|
39
63
|
|
|
40
64
|
```js
|
|
@@ -44,25 +68,30 @@ const greencheck = require('@tgwf/hosting')
|
|
|
44
68
|
// returns true if green, otherwise false
|
|
45
69
|
greencheck.check("google.com")
|
|
46
70
|
|
|
47
|
-
// returns an array of the green domains, in this case ["google.
|
|
48
|
-
greencheck.check(["google.com", "kochindustries.com"])
|
|
71
|
+
// returns an array of the green domains, in this case ["google.com"].
|
|
72
|
+
greencheck.check(["google.com", "kochindustries.com"])]
|
|
49
73
|
|
|
50
74
|
// returns an array of green domains, again in this case, ["google.com"]
|
|
51
75
|
greencheck.checkPage(["google.com"])
|
|
52
76
|
|
|
53
77
|
```
|
|
54
78
|
|
|
55
|
-
|
|
79
|
+
### Notes
|
|
80
|
+
|
|
81
|
+
Please note, we currently look at just the carbon cost of _generating_ the electricity, similar to how the [International Energy Agency (IEA)] does, not the full life cycle cost of the energy.
|
|
82
|
+
|
|
83
|
+
Doing this would include things like:
|
|
56
84
|
|
|
57
|
-
- the carbon associated with digging up the fuel
|
|
58
|
-
- the carbon associated with mining the materials to
|
|
85
|
+
- the carbon emitted when carrying out activity associated with digging up the fuel
|
|
86
|
+
- the carbon associated with mining the materials to _build_ the power stations, datacentres, and so on
|
|
59
87
|
- the end of life costs
|
|
60
88
|
- the maintenance costs over the life of the datacentres, power generation and end user devices, and the rest of the internet
|
|
61
89
|
|
|
62
|
-
|
|
90
|
+
Life cycle figures do exist, but they are very difficult to do well. If you're interested in contributing to this, we'd love to hear from you.
|
|
63
91
|
|
|
64
92
|
|
|
65
93
|
# Licenses
|
|
66
94
|
|
|
67
95
|
Apache 2.0
|
|
68
96
|
|
|
97
|
+
[International Energy Agency (IEA)]: https://www.iea.org/
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
["google.com","maxcdn.bootstrapcdn.com","thegreenwebfoundation.org","www.thegreenwebfoundation.org","fonts.googleapis.com","ajax.googleapis.com","assets.digitalclimatestrike.net","cdnjs.cloudflare.com","graphite.thegreenwebfoundation.org","analytics.thegreenwebfoundation.org","fonts.gstatic.com","api.thegreenwebfoundation.org"]
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tgwf/co2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Work out the co2 of your digital services",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest",
|
|
8
8
|
"lint": "eslint .",
|
|
9
|
-
"lint:fix": "eslint . --fix"
|
|
9
|
+
"lint:fix": "eslint . --fix",
|
|
10
|
+
"travis": "npm run lint && jest"
|
|
10
11
|
},
|
|
11
12
|
"keywords": [
|
|
12
13
|
"sustainability",
|
|
@@ -20,14 +21,17 @@
|
|
|
20
21
|
"author": "Chris Adams",
|
|
21
22
|
"license": "Apache-2.0",
|
|
22
23
|
"devDependencies": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"eslint": "
|
|
27
|
-
"prettier": "
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
24
|
+
"@tgwf/url2green": "^0.4.0",
|
|
25
|
+
"eslint": "^8.3.0",
|
|
26
|
+
"eslint-config-prettier": "^8.3.0",
|
|
27
|
+
"eslint-plugin-jest": "^25.2.4",
|
|
28
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
29
|
+
"jest": "^27.3.1",
|
|
30
|
+
"minimist": "^1.2.5",
|
|
31
|
+
"nock": "^13.2.1",
|
|
32
|
+
"np": "^7.6.0",
|
|
33
|
+
"pagexray": "^4.3.1",
|
|
34
|
+
"prettier": "^2.4.1"
|
|
31
35
|
},
|
|
32
36
|
"np": {
|
|
33
37
|
"yarn": false
|
|
@@ -36,7 +40,10 @@
|
|
|
36
40
|
"access": "public"
|
|
37
41
|
},
|
|
38
42
|
"dependencies": {
|
|
39
|
-
"better-sqlite3": "^5.4.3",
|
|
40
43
|
"debug": "^4.1.1"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/thegreenwebfoundation/co2.js.git"
|
|
41
48
|
}
|
|
42
49
|
}
|
package/src/1byte.js
CHANGED
|
@@ -9,19 +9,28 @@ const CO2_PER_KWH_IN_DC_GREY = 519;
|
|
|
9
9
|
|
|
10
10
|
// The device usage figure combines figures for:
|
|
11
11
|
// 1. the usage for devices (which is small proportion of the energy use)
|
|
12
|
-
// 2. the *making* the device, which is
|
|
12
|
+
// 2. the *making* the device, which is comparatively high.
|
|
13
13
|
|
|
14
|
-
const KWH_PER_BYTE_IN_DC =
|
|
14
|
+
const KWH_PER_BYTE_IN_DC = 7.2e-11;
|
|
15
15
|
|
|
16
16
|
// this is probably best left as something users can define, or
|
|
17
17
|
// a weighted average based on total usage.
|
|
18
|
+
// Using a simple mean for now, as while web traffic to end users might trend
|
|
19
|
+
// towards wifi and mobile,
|
|
20
|
+
// Web traffic between servers is likely wired networks
|
|
21
|
+
|
|
22
|
+
const FIXED_NETWORK_WIRED = 4.29e-10;
|
|
23
|
+
const FIXED_NETWORK_WIFI = 1.52e-10;
|
|
24
|
+
const FOUR_G_MOBILE = 8.84e-10;
|
|
25
|
+
|
|
18
26
|
// Pull requests gratefully accepted
|
|
19
|
-
const KWH_PER_BYTE_FOR_NETWORK =
|
|
27
|
+
const KWH_PER_BYTE_FOR_NETWORK =
|
|
28
|
+
(FIXED_NETWORK_WIRED + FIXED_NETWORK_WIFI + FOUR_G_MOBILE) / 3;
|
|
20
29
|
|
|
21
|
-
const KWH_PER_BYTE_FOR_DEVICES =
|
|
30
|
+
const KWH_PER_BYTE_FOR_DEVICES = 1.3e-10;
|
|
22
31
|
module.exports = {
|
|
23
32
|
KWH_PER_BYTE_IN_DC,
|
|
24
33
|
KWH_PER_BYTE_FOR_NETWORK,
|
|
25
34
|
KWH_PER_BYTE_FOR_DEVICES,
|
|
26
|
-
CO2_PER_KWH_IN_DC_GREY
|
|
35
|
+
CO2_PER_KWH_IN_DC_GREY,
|
|
27
36
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const oneByte = require("./1byte");
|
|
4
|
+
|
|
5
|
+
describe("onebyte", function () {
|
|
6
|
+
describe("perByte", function () {
|
|
7
|
+
it.only("returns a simple average of the different networks", function () {
|
|
8
|
+
// we limit this to 12 figures with toFixed(12), because
|
|
9
|
+
// we have a recurring 333333 afterwards
|
|
10
|
+
// 4.88e-10 is the same as 0.000000000488
|
|
11
|
+
const expected_val = (0.000000000488).toFixed(12);
|
|
12
|
+
|
|
13
|
+
expect(oneByte.KWH_PER_BYTE_FOR_NETWORK.toFixed(12)).toBe(expected_val);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
});
|
package/src/co2.js
CHANGED
|
@@ -57,10 +57,10 @@ class CO2 {
|
|
|
57
57
|
co2PerDomain.push({
|
|
58
58
|
domain,
|
|
59
59
|
co2,
|
|
60
|
-
transferSize: pageXray.domains[domain].transferSize
|
|
60
|
+
transferSize: pageXray.domains[domain].transferSize,
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
co2PerDomain.sort(function(a, b) {
|
|
63
|
+
co2PerDomain.sort(function (a, b) {
|
|
64
64
|
return b.co2 - a.co2;
|
|
65
65
|
});
|
|
66
66
|
|
|
@@ -105,10 +105,10 @@ class CO2 {
|
|
|
105
105
|
all.push({
|
|
106
106
|
type,
|
|
107
107
|
co2: co2PerContentType[type].co2,
|
|
108
|
-
transferSize: co2PerContentType[type].transferSize
|
|
108
|
+
transferSize: co2PerContentType[type].transferSize,
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
-
all.sort(function(a, b) {
|
|
111
|
+
all.sort(function (a, b) {
|
|
112
112
|
return b.co2 - a.co2;
|
|
113
113
|
});
|
|
114
114
|
return all;
|
|
@@ -125,7 +125,7 @@ class CO2 {
|
|
|
125
125
|
);
|
|
126
126
|
allAssets.push({ url: asset.url, co2: co2ForTransfer, transferSize });
|
|
127
127
|
}
|
|
128
|
-
allAssets.sort(function(a, b) {
|
|
128
|
+
allAssets.sort(function (a, b) {
|
|
129
129
|
return b.co2 - a.co2;
|
|
130
130
|
});
|
|
131
131
|
|
package/src/co2.test.js
CHANGED
|
@@ -6,137 +6,145 @@ const path = require("path");
|
|
|
6
6
|
const CO2 = require("./co2");
|
|
7
7
|
const pagexray = require("pagexray");
|
|
8
8
|
|
|
9
|
-
describe("
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const TGWF_MIXED_VALUE = 1.6706517455999996;
|
|
9
|
+
describe("co2", function () {
|
|
10
|
+
let har, co2;
|
|
11
|
+
const TGWF_GREY_VALUE = 0.20497;
|
|
12
|
+
const TGWF_GREEN_VALUE = 0.54704;
|
|
13
|
+
const TGWF_MIXED_VALUE = 0.16718;
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const MILLION = 1000000;
|
|
16
|
+
const MILLION_GREY = 0.29081;
|
|
17
|
+
const MILLION_GREEN = 0.23196;
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
beforeEach(function () {
|
|
20
|
+
co2 = new CO2();
|
|
21
|
+
har = JSON.parse(
|
|
22
|
+
fs.readFileSync(
|
|
23
|
+
path.resolve(__dirname, "../data/fixtures/tgwf.har"),
|
|
24
|
+
"utf8"
|
|
25
|
+
)
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("perByte", function () {
|
|
30
|
+
it("returns a CO2 number for data transfer using 'grey' power", function () {
|
|
31
|
+
expect(co2.perByte(MILLION).toPrecision(5)).toBe(
|
|
32
|
+
MILLION_GREY.toPrecision(5)
|
|
27
33
|
);
|
|
28
34
|
});
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
expect(co2.perByte(MILLION, true)).toBe(MILLION_GREEN);
|
|
38
|
-
});
|
|
36
|
+
it("returns a lower CO2 number for data transfer from domains using entirely 'green' power", function () {
|
|
37
|
+
expect(co2.perByte(MILLION, false).toPrecision(5)).toBe(
|
|
38
|
+
MILLION_GREY.toPrecision(5)
|
|
39
|
+
);
|
|
40
|
+
expect(co2.perByte(MILLION, true).toPrecision(5)).toBe(
|
|
41
|
+
MILLION_GREEN.toPrecision(5)
|
|
42
|
+
);
|
|
39
43
|
});
|
|
44
|
+
});
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
describe("perPage", function () {
|
|
47
|
+
it("returns CO2 for total transfer for page", function () {
|
|
48
|
+
const pages = pagexray.convert(har);
|
|
49
|
+
const pageXrayRun = pages[0];
|
|
45
50
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
51
|
+
expect(co2.perPage(pageXrayRun).toPrecision(5)).toBe(
|
|
52
|
+
TGWF_GREY_VALUE.toPrecision(5)
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
it("returns lower CO2 for page served from green site", function () {
|
|
56
|
+
const pages = pagexray.convert(har);
|
|
57
|
+
const pageXrayRun = pages[0];
|
|
58
|
+
let green = [
|
|
59
|
+
"www.thegreenwebfoundation.org",
|
|
60
|
+
"fonts.googleapis.com",
|
|
61
|
+
"ajax.googleapis.com",
|
|
62
|
+
"assets.digitalclimatestrike.net",
|
|
63
|
+
"cdnjs.cloudflare.com",
|
|
64
|
+
"graphite.thegreenwebfoundation.org",
|
|
65
|
+
"analytics.thegreenwebfoundation.org",
|
|
66
|
+
"fonts.gstatic.com",
|
|
67
|
+
"api.thegreenwebfoundation.org",
|
|
68
|
+
];
|
|
69
|
+
expect(co2.perPage(pageXrayRun, green)).toBeLessThan(TGWF_GREY_VALUE);
|
|
70
|
+
});
|
|
71
|
+
it("returns a lower CO2 number where *some* domains use green power", function () {
|
|
72
|
+
const pages = pagexray.convert(har);
|
|
73
|
+
const pageXrayRun = pages[0];
|
|
74
|
+
// green can be true, or a array containing entries
|
|
75
|
+
let green = [
|
|
76
|
+
"www.thegreenwebfoundation.org",
|
|
77
|
+
"fonts.googleapis.com",
|
|
78
|
+
"ajax.googleapis.com",
|
|
79
|
+
"assets.digitalclimatestrike.net",
|
|
80
|
+
"cdnjs.cloudflare.com",
|
|
81
|
+
"graphite.thegreenwebfoundation.org",
|
|
82
|
+
"analytics.thegreenwebfoundation.org",
|
|
83
|
+
"fonts.gstatic.com",
|
|
84
|
+
"api.thegreenwebfoundation.org",
|
|
85
|
+
];
|
|
86
|
+
expect(co2.perPage(pageXrayRun, green).toPrecision(5)).toBe(
|
|
87
|
+
TGWF_MIXED_VALUE.toPrecision(5)
|
|
88
|
+
);
|
|
81
89
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
});
|
|
91
|
+
describe("perDomain", function () {
|
|
92
|
+
it("shows object listing Co2 for each domain", function () {
|
|
93
|
+
const pages = pagexray.convert(har);
|
|
94
|
+
const pageXrayRun = pages[0];
|
|
95
|
+
const res = co2.perDomain(pageXrayRun);
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
const domains = [
|
|
98
|
+
"thegreenwebfoundation.org",
|
|
99
|
+
"www.thegreenwebfoundation.org",
|
|
100
|
+
"maxcdn.bootstrapcdn.com",
|
|
101
|
+
"fonts.googleapis.com",
|
|
102
|
+
"ajax.googleapis.com",
|
|
103
|
+
"assets.digitalclimatestrike.net",
|
|
104
|
+
"cdnjs.cloudflare.com",
|
|
105
|
+
"graphite.thegreenwebfoundation.org",
|
|
106
|
+
"analytics.thegreenwebfoundation.org",
|
|
107
|
+
"fonts.gstatic.com",
|
|
108
|
+
"api.thegreenwebfoundation.org",
|
|
109
|
+
];
|
|
101
110
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
for (let obj of res) {
|
|
112
|
+
expect(domains.indexOf(obj.domain)).toBeGreaterThan(-1);
|
|
113
|
+
expect(typeof obj.co2).toBe("number");
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
it("shows lower Co2 for green domains", function () {
|
|
117
|
+
const pages = pagexray.convert(har);
|
|
118
|
+
const pageXrayRun = pages[0];
|
|
110
119
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
120
|
+
const greenDomains = [
|
|
121
|
+
"www.thegreenwebfoundation.org",
|
|
122
|
+
"fonts.googleapis.com",
|
|
123
|
+
"ajax.googleapis.com",
|
|
124
|
+
"assets.digitalclimatestrike.net",
|
|
125
|
+
"cdnjs.cloudflare.com",
|
|
126
|
+
"graphite.thegreenwebfoundation.org",
|
|
127
|
+
"analytics.thegreenwebfoundation.org",
|
|
128
|
+
"fonts.gstatic.com",
|
|
129
|
+
"api.thegreenwebfoundation.org",
|
|
130
|
+
];
|
|
131
|
+
const res = co2.perDomain(pageXrayRun);
|
|
132
|
+
const resWithGreen = co2.perDomain(pageXrayRun, greenDomains);
|
|
124
133
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
});
|
|
134
|
+
for (let obj of res) {
|
|
135
|
+
expect(typeof obj.co2).toBe("number");
|
|
136
|
+
}
|
|
137
|
+
for (let obj of greenDomains) {
|
|
138
|
+
let index = 0;
|
|
139
|
+
expect(resWithGreen[index].co2).toBeLessThan(res[index].co2);
|
|
140
|
+
index++;
|
|
141
|
+
}
|
|
134
142
|
});
|
|
135
|
-
// describe('perContentType', function () {
|
|
136
|
-
// test.skip('shows a breakdown of emissions by content type');
|
|
137
|
-
// });
|
|
138
|
-
// describe('dirtiestResources', function () {
|
|
139
|
-
// it.skip('shows the top 10 resources by CO2 emissions');
|
|
140
|
-
// });
|
|
141
143
|
});
|
|
144
|
+
// describe('perContentType', function () {
|
|
145
|
+
// test.skip('shows a breakdown of emissions by content type');
|
|
146
|
+
// });
|
|
147
|
+
// describe('dirtiestResources', function () {
|
|
148
|
+
// it.skip('shows the top 10 resources by CO2 emissions');
|
|
149
|
+
// });
|
|
142
150
|
});
|
package/src/green-byte.js
CHANGED
|
@@ -28,7 +28,6 @@ async function checkDomainsAgainstAPI(domains) {
|
|
|
28
28
|
)}`
|
|
29
29
|
)
|
|
30
30
|
);
|
|
31
|
-
|
|
32
31
|
return greenDomainsFromResults(allGreenCheckResults);
|
|
33
32
|
} catch (e) {
|
|
34
33
|
return [];
|
|
@@ -37,20 +36,19 @@ async function checkDomainsAgainstAPI(domains) {
|
|
|
37
36
|
|
|
38
37
|
function greenDomainsFromResults(greenResults) {
|
|
39
38
|
const entries = Object.entries(greenResults);
|
|
40
|
-
let greenEntries = entries.filter(function([key, val]) {
|
|
39
|
+
let greenEntries = entries.filter(function ([key, val]) {
|
|
41
40
|
return val.green;
|
|
42
41
|
});
|
|
43
|
-
|
|
44
|
-
return greenEntries.map(function([key, val]) {
|
|
42
|
+
return greenEntries.map(function ([key, val]) {
|
|
45
43
|
return val.url;
|
|
46
44
|
});
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
async function getBody(url) {
|
|
50
48
|
// Return new promise
|
|
51
|
-
return new Promise(function(resolve, reject) {
|
|
49
|
+
return new Promise(function (resolve, reject) {
|
|
52
50
|
// Do async job
|
|
53
|
-
const req = https.get(url, function(res) {
|
|
51
|
+
const req = https.get(url, function (res) {
|
|
54
52
|
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
55
53
|
log(
|
|
56
54
|
"Could not get info from the Green Web Foundation API, %s for %s",
|
|
@@ -61,7 +59,7 @@ async function getBody(url) {
|
|
|
61
59
|
}
|
|
62
60
|
const data = [];
|
|
63
61
|
|
|
64
|
-
res.on("data", chunk => {
|
|
62
|
+
res.on("data", (chunk) => {
|
|
65
63
|
data.push(chunk);
|
|
66
64
|
});
|
|
67
65
|
|
|
@@ -72,5 +70,5 @@ async function getBody(url) {
|
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
module.exports = {
|
|
75
|
-
check
|
|
73
|
+
check,
|
|
76
74
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const hosting = require("./hosting-api");
|
|
4
|
+
const nock = require("nock");
|
|
5
|
+
|
|
6
|
+
describe("hostingAPI", function () {
|
|
7
|
+
describe("checking a single domain with #check", function () {
|
|
8
|
+
it("using the API", async function () {
|
|
9
|
+
const scope = nock("https://api.thegreenwebfoundation.org/")
|
|
10
|
+
.get("/greencheck/google.com")
|
|
11
|
+
.reply(200, {
|
|
12
|
+
url: "google.com",
|
|
13
|
+
green: true,
|
|
14
|
+
});
|
|
15
|
+
const res = await hosting.check("google.com");
|
|
16
|
+
expect(res).toEqual(true);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe("implicitly checking multiple domains with #check", function () {
|
|
20
|
+
it("using the API", async function () {
|
|
21
|
+
const scope = nock("https://api.thegreenwebfoundation.org/")
|
|
22
|
+
.get("/v2/greencheckmulti/[%22google.com%22,%22kochindustries.com%22]")
|
|
23
|
+
.reply(200, {
|
|
24
|
+
"google.com": {
|
|
25
|
+
url: "google.com",
|
|
26
|
+
green: true,
|
|
27
|
+
},
|
|
28
|
+
"kochindustries.com": {
|
|
29
|
+
url: "kochindustries.com",
|
|
30
|
+
green: null,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
const res = await hosting.check(["google.com", "kochindustries.com"]);
|
|
34
|
+
expect(res).toContain("google.com");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const log = require("debug")("tgwf:url2green:test");
|
|
4
|
+
const { hosting } = require("@tgwf/url2green");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const dbPath = path.resolve(
|
|
8
|
+
__dirname,
|
|
9
|
+
"..",
|
|
10
|
+
"data",
|
|
11
|
+
"fixtures",
|
|
12
|
+
"url2green.test.db"
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
describe("hostingDatabase", function () {
|
|
16
|
+
describe("checking a single domain with #check", function () {
|
|
17
|
+
test("tries to use a local database if available ", async function () {
|
|
18
|
+
const res = await hosting.check("google.com", dbPath);
|
|
19
|
+
expect(res).toEqual(true);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe("implicitly checking multiple domains with #check", function () {
|
|
23
|
+
test("tries to use a local database if available", async function () {
|
|
24
|
+
const res = await hosting.check(
|
|
25
|
+
["google.com", "kochindustries.com"],
|
|
26
|
+
dbPath
|
|
27
|
+
);
|
|
28
|
+
expect(res).toContain("google.com");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const log = require("debug")("tgwf:hostingCache");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const zlib = require("zlib");
|
|
7
|
+
const { promisify } = require("util");
|
|
8
|
+
const readFile = promisify(fs.readFile);
|
|
9
|
+
const gunzip = promisify(zlib.gunzip);
|
|
10
|
+
|
|
11
|
+
async function streamToString(stream) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const chunks = [];
|
|
14
|
+
stream.on("error", reject);
|
|
15
|
+
stream.on("data", (chunk) => chunks.push(chunk));
|
|
16
|
+
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function getGzippedFileAsJson(jsonPath) {
|
|
21
|
+
const readStream = fs.createReadStream(jsonPath);
|
|
22
|
+
const text = await streamToString(readStream);
|
|
23
|
+
const unzipped = await gunzip(text);
|
|
24
|
+
return unzipped.toString();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function loadJSON(jsonPath) {
|
|
28
|
+
const jsonBuffer = jsonPath.endsWith(".gz")
|
|
29
|
+
? await getGzippedFileAsJson(jsonPath)
|
|
30
|
+
: await readFile(jsonPath);
|
|
31
|
+
return JSON.parse(jsonBuffer);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function check(domain, db) {
|
|
35
|
+
// is it a single domain or an array of them?
|
|
36
|
+
if (typeof domain === "string") {
|
|
37
|
+
return checkInJSON(domain, db);
|
|
38
|
+
} else {
|
|
39
|
+
return checkDomainsInJSON(domain, db);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function checkInJSON(domain, db) {
|
|
44
|
+
if (db.indexOf(domain) > -1) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function greenDomainsFromResults(greenResults) {
|
|
51
|
+
const entries = Object.entries(greenResults);
|
|
52
|
+
let greenEntries = entries.filter(function ([key, val]) {
|
|
53
|
+
return val.green;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return greenEntries.map(function ([key, val]) {
|
|
57
|
+
return val.url;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function checkDomainsInJSON(domains, db) {
|
|
62
|
+
let greenDomains = [];
|
|
63
|
+
|
|
64
|
+
for (let domain of domains) {
|
|
65
|
+
if (db.indexOf(domain) > -1) {
|
|
66
|
+
greenDomains.push(domain);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return greenDomains;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
check,
|
|
74
|
+
loadJSON,
|
|
75
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const hosting = require("./hosting-json");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
describe("hostingJSON", function () {
|
|
7
|
+
const jsonPath = path.resolve(
|
|
8
|
+
__dirname,
|
|
9
|
+
"..",
|
|
10
|
+
"data",
|
|
11
|
+
"fixtures",
|
|
12
|
+
"url2green.test.json"
|
|
13
|
+
);
|
|
14
|
+
const jsonPathGz = path.resolve(
|
|
15
|
+
__dirname,
|
|
16
|
+
"..",
|
|
17
|
+
"data",
|
|
18
|
+
"fixtures",
|
|
19
|
+
"url2green.test.json.gz"
|
|
20
|
+
);
|
|
21
|
+
describe("checking a single domain with #check", function () {
|
|
22
|
+
test("against the list of domains as JSON", async function () {
|
|
23
|
+
const db = await hosting.loadJSON(jsonPath);
|
|
24
|
+
const res = await hosting.check("google.com", db);
|
|
25
|
+
expect(res).toEqual(true);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe("checking a single domain with #check", function () {
|
|
29
|
+
test("against the list of domains as JSON loaded from a gzipped JSON", async function () {
|
|
30
|
+
const db = await hosting.loadJSON(jsonPathGz);
|
|
31
|
+
const res = await hosting.check("google.com", db);
|
|
32
|
+
expect(res).toEqual(true);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("implicitly checking multiple domains with #check", function () {
|
|
36
|
+
test("against the list of domains as JSON", async function () {
|
|
37
|
+
const db = await hosting.loadJSON(jsonPath);
|
|
38
|
+
const domains = ["google.com", "kochindustries.com"];
|
|
39
|
+
|
|
40
|
+
const res = await hosting.check(domains, db);
|
|
41
|
+
expect(res).toContain("google.com");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
package/src/hosting.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const hostingAPI = require("./
|
|
5
|
-
const
|
|
3
|
+
const log = require("debug")("tgwf:hosting");
|
|
4
|
+
const hostingAPI = require("./hosting-api");
|
|
5
|
+
const hostingJSON = require("./hosting-json");
|
|
6
6
|
|
|
7
|
-
function check(domain,
|
|
8
|
-
if (
|
|
9
|
-
return
|
|
7
|
+
function check(domain, db) {
|
|
8
|
+
if (db) {
|
|
9
|
+
return hostingJSON.check(domain, db);
|
|
10
10
|
} else {
|
|
11
11
|
return hostingAPI.check(domain);
|
|
12
12
|
}
|
|
@@ -14,22 +14,23 @@ function check(domain, dbName) {
|
|
|
14
14
|
|
|
15
15
|
function greenDomainsFromResults(greenResults) {
|
|
16
16
|
const entries = Object.entries(greenResults);
|
|
17
|
-
let greenEntries = entries.filter(function([key, val]) {
|
|
17
|
+
let greenEntries = entries.filter(function ([key, val]) {
|
|
18
18
|
return val.green;
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
return greenEntries.map(function([key, val]) {
|
|
21
|
+
return greenEntries.map(function ([key, val]) {
|
|
22
22
|
return val.url;
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
async function checkPage(pageXray) {
|
|
26
|
+
async function checkPage(pageXray, db) {
|
|
27
27
|
const domains = Object.keys(pageXray.domains);
|
|
28
|
-
return check(domains);
|
|
28
|
+
return check(domains, db);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
module.exports = {
|
|
32
32
|
check,
|
|
33
33
|
checkPage,
|
|
34
|
-
greenDomains: greenDomainsFromResults
|
|
34
|
+
greenDomains: greenDomainsFromResults,
|
|
35
|
+
loadJSON: hostingJSON.loadJSON,
|
|
35
36
|
};
|
package/src/hosting.test.js
CHANGED
|
@@ -6,9 +6,17 @@ const path = require("path");
|
|
|
6
6
|
const hosting = require("./hosting");
|
|
7
7
|
const pagexray = require("pagexray");
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const jsonPath = path.resolve(
|
|
10
|
+
__dirname,
|
|
11
|
+
"..",
|
|
12
|
+
"data",
|
|
13
|
+
"fixtures",
|
|
14
|
+
"url2green.test.json"
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
describe("hosting", function () {
|
|
10
18
|
let har;
|
|
11
|
-
beforeEach(function() {
|
|
19
|
+
beforeEach(function () {
|
|
12
20
|
har = JSON.parse(
|
|
13
21
|
fs.readFileSync(
|
|
14
22
|
path.resolve(__dirname, "../data/fixtures/tgwf.har"),
|
|
@@ -16,17 +24,16 @@ describe("hosting", function() {
|
|
|
16
24
|
)
|
|
17
25
|
);
|
|
18
26
|
});
|
|
19
|
-
describe("checking all domains on a page object with #checkPage ", function() {
|
|
20
|
-
it("it returns a list of green domains, when passed a page object", async function() {
|
|
27
|
+
describe("checking all domains on a page object with #checkPage ", function () {
|
|
28
|
+
it("it returns a list of green domains, when passed a page object", async function () {
|
|
21
29
|
const pages = pagexray.convert(har);
|
|
22
30
|
const pageXrayRun = pages[0];
|
|
31
|
+
const db = await hosting.loadJSON(jsonPath);
|
|
32
|
+
const greenDomains = await hosting.checkPage(pageXrayRun, db);
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
const greenDomains = await hosting.checkPage(pageXrayRun);
|
|
26
|
-
|
|
27
|
-
expect(greenDomains).toHaveLength(10);
|
|
28
|
-
|
|
34
|
+
expect(greenDomains).toHaveLength(11);
|
|
29
35
|
const expectedGreendomains = [
|
|
36
|
+
"maxcdn.bootstrapcdn.com",
|
|
30
37
|
"thegreenwebfoundation.org",
|
|
31
38
|
"www.thegreenwebfoundation.org",
|
|
32
39
|
"fonts.googleapis.com",
|
|
@@ -36,9 +43,9 @@ describe("hosting", function() {
|
|
|
36
43
|
"graphite.thegreenwebfoundation.org",
|
|
37
44
|
"analytics.thegreenwebfoundation.org",
|
|
38
45
|
"fonts.gstatic.com",
|
|
39
|
-
"api.thegreenwebfoundation.org"
|
|
46
|
+
"api.thegreenwebfoundation.org",
|
|
40
47
|
];
|
|
41
|
-
greenDomains.forEach(function(dom) {
|
|
48
|
+
greenDomains.forEach(function (dom) {
|
|
42
49
|
expect(expectedGreendomains).toContain(dom);
|
|
43
50
|
});
|
|
44
51
|
});
|
|
@@ -46,42 +53,25 @@ describe("hosting", function() {
|
|
|
46
53
|
// 'it returns an empty list, when passed a page object with no green domains'
|
|
47
54
|
// );
|
|
48
55
|
});
|
|
49
|
-
describe("checking a single domain with #check", function() {
|
|
50
|
-
it("
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
path.resolve(__dirname, "..", "url2green.test.db")
|
|
54
|
-
);
|
|
55
|
-
expect(res).toEqual(true);
|
|
56
|
-
});
|
|
57
|
-
it("use the API instead", async function() {
|
|
58
|
-
const res = await hosting.check("google.com");
|
|
56
|
+
describe("checking a single domain with #check", function () {
|
|
57
|
+
it("use the API instead", async function () {
|
|
58
|
+
const db = await hosting.loadJSON(jsonPath);
|
|
59
|
+
const res = await hosting.check("google.com", db);
|
|
59
60
|
expect(res).toEqual(true);
|
|
60
61
|
});
|
|
61
62
|
});
|
|
62
|
-
describe("implicitly checking multiple domains with #check", function() {
|
|
63
|
-
it("
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
);
|
|
68
|
-
expect(res).toContain("google.com");
|
|
69
|
-
});
|
|
70
|
-
it("Use the API", async function() {
|
|
71
|
-
const res = await hosting.check(["google.com", "kochindustries.com"]);
|
|
63
|
+
describe("implicitly checking multiple domains with #check", function () {
|
|
64
|
+
it("Use the API", async function () {
|
|
65
|
+
const db = await hosting.loadJSON(jsonPath);
|
|
66
|
+
|
|
67
|
+
const res = await hosting.check(["google.com", "kochindustries.com"], db);
|
|
72
68
|
expect(res).toContain("google.com");
|
|
73
69
|
});
|
|
74
70
|
});
|
|
75
|
-
describe("explicitly checking multiple domains with #checkMulti", function() {
|
|
76
|
-
it("
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
path.resolve(__dirname, "..", "url2green.test.db")
|
|
80
|
-
);
|
|
81
|
-
expect(res).toContain("google.com");
|
|
82
|
-
});
|
|
83
|
-
it("use the API", async function() {
|
|
84
|
-
const res = await hosting.check(["google.com", "kochindustries.com"]);
|
|
71
|
+
describe("explicitly checking multiple domains with #checkMulti", function () {
|
|
72
|
+
it("use the API", async function () {
|
|
73
|
+
const db = await hosting.loadJSON(jsonPath);
|
|
74
|
+
const res = await hosting.check(["google.com", "kochindustries.com"], db);
|
|
85
75
|
expect(res).toContain("google.com");
|
|
86
76
|
});
|
|
87
77
|
});
|
package/src/index.js
CHANGED
package/.travis.yml
DELETED
package/src/hostingAPI.test.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const hosting = require("./hostingAPI");
|
|
4
|
-
|
|
5
|
-
describe("hostingAPI", function() {
|
|
6
|
-
describe("checking a single domain with #check", function() {
|
|
7
|
-
it("using the API", async function() {
|
|
8
|
-
const res = await hosting.check("google.com");
|
|
9
|
-
expect(res).toEqual(true);
|
|
10
|
-
});
|
|
11
|
-
});
|
|
12
|
-
describe("implicitly checking multiple domains with #check", function() {
|
|
13
|
-
it("using the API", async function() {
|
|
14
|
-
const res = await hosting.check(["google.com", "kochindustries.com"]);
|
|
15
|
-
expect(res).toContain("google.com");
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
});
|
package/src/hostingDatabase.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const log = require("debug")("tgwf:hostingDatabase");
|
|
4
|
-
const Database = require("better-sqlite3");
|
|
5
|
-
|
|
6
|
-
function getQ(domains) {
|
|
7
|
-
const q = [];
|
|
8
|
-
for (let domain of domains) {
|
|
9
|
-
q.push("?");
|
|
10
|
-
}
|
|
11
|
-
return q.join(",");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function getDatabase(databaseFullPathAndName) {
|
|
15
|
-
log(`looking for db at ${databaseFullPathAndName}`);
|
|
16
|
-
return new Database(databaseFullPathAndName, {
|
|
17
|
-
readonly: true,
|
|
18
|
-
fileMustExist: true
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function check(domain, dbName) {
|
|
23
|
-
let db;
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
db = getDatabase(dbName);
|
|
27
|
-
} catch (SqliteError) {
|
|
28
|
-
log(`couldn't find SQlite database at path: ${dbName}`);
|
|
29
|
-
throw SqliteError;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// is it a single domain or an array of them?
|
|
33
|
-
if (typeof domain === "string") {
|
|
34
|
-
return checkInDB(domain, db);
|
|
35
|
-
} else {
|
|
36
|
-
return checkDomainsInDB(domain, db);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function checkInDB(domain, db) {
|
|
41
|
-
try {
|
|
42
|
-
const stmt = db.prepare("SELECT * FROM green_presenting WHERE url = ?");
|
|
43
|
-
return !!stmt.get(domain).green;
|
|
44
|
-
} finally {
|
|
45
|
-
if (db) {
|
|
46
|
-
db.close();
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function greenDomainsFromResults(greenResults) {
|
|
52
|
-
const entries = Object.entries(greenResults);
|
|
53
|
-
let greenEntries = entries.filter(function([key, val]) {
|
|
54
|
-
return val.green;
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
return greenEntries.map(function([key, val]) {
|
|
58
|
-
return val.url;
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function checkDomainsInDB(domains, db) {
|
|
63
|
-
try {
|
|
64
|
-
const stmt = db.prepare(
|
|
65
|
-
`SELECT * FROM green_presenting WHERE url in (${getQ(domains)})`
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const res = stmt.all(domains);
|
|
69
|
-
|
|
70
|
-
return greenDomainsFromResults(res);
|
|
71
|
-
} finally {
|
|
72
|
-
if (db) {
|
|
73
|
-
db.close();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
module.exports = {
|
|
79
|
-
check
|
|
80
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const hosting = require("./hostingDatabase");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
|
|
6
|
-
describe("hostingDatabase", function() {
|
|
7
|
-
describe("checking a single domain with #check", function() {
|
|
8
|
-
it("tries to use a local database if available ", async function() {
|
|
9
|
-
const res = await hosting.check(
|
|
10
|
-
"google.com",
|
|
11
|
-
path.resolve(__dirname, "..", "url2green.test.db")
|
|
12
|
-
);
|
|
13
|
-
expect(res).toEqual(true);
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
describe("implicitly checking multiple domains with #check", function() {
|
|
17
|
-
it("tries to use a local database if available", async function() {
|
|
18
|
-
const res = await hosting.check(
|
|
19
|
-
["google.com", "kochindustries.com"],
|
|
20
|
-
path.resolve(__dirname, "..", "url2green.test.db")
|
|
21
|
-
);
|
|
22
|
-
expect(res).toContain("google.com");
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|
package/url2green.test.db
DELETED
|
Binary file
|