Skip to content

Commit

Permalink
Feature/stub (#1)
Browse files Browse the repository at this point in the history
* add stub registration

* update docs
  • Loading branch information
alextremp authored Jun 23, 2020
1 parent e411b87 commit 9300042
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 16 deletions.
6 changes: 5 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
src
deploy
coverage
.nyc_output
.travis.yml
Expand All @@ -8,4 +9,7 @@ coverage
resources
package
*.tgz
versiona.js
versiona.js
deploy.js
mocha.config.js
.babelrc.js
45 changes: 40 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
![](/resources/logo/boros_logo.png)

# Boros TCF Stub

[![Build status](https://travis-ci.org/scm-spain/boros-tcf-stub.svg?branch=master)](https://travis-ci.org/scm-spain/boros-tcf-stub)
Expand All @@ -11,19 +9,56 @@

* [About](#about)
* [Features](#features)
* [Technical features](#technical-features)
* [Configuration](#configuration)
* [Usage](#usage)
* [License](#license)


## About

The Boros TCF stub implements the [standard TCF v2 stub](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#how-does-the-cmp-stub-api-work)

## Features

## Technical features
- Registers the `__tcfapiLocator` frame

- Stubs the `window.__tcfapi` responding immediately to the commands
- `ping` [See PingReturn in the stubbed __tcfapi](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#requirements-for-the-cmp-stub-api-script)
- `pending` returns the pending calls accumulated while calling `window.__tcfapi` commands

- Initializes the cross-framee communication via `postMessagee`, [see usage details](https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#how-can-vendors-that-use-iframes-call-the-cmp-api-from-an-iframe)

## Usage

### As an importable module

> Use it this way if you're generating your own initialization
**Install**
```
npm i @adv-ui/boros-tcf-stub --save
```

**Register the Stub**
```
import registerStub from '../main'
// do your magic
registerStub()
```

> Remember that the Stub **must** be registered before any script depending on the TCF is loaded
### As a standalone script

**Add it to the `head` tag**

```
<script
src="https://c.dcdn.es/borostcf/stub/BorosTcfStub.pro.js"
async="false"
/>
```

## License
Boros TCF Stub is [MIT licensed](./LICENSE).
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
"description": "Adevinta GDPR - Transparency and Consent Framework - Stub for Boros TCF",
"main": "dist",
"scripts": {
"build": "rm -Rf dist && babel src/main --out-dir dist",
"check": "npm run lint && npm run test",
"coverage": "nyc --reporter=html --exclude='\"src/test/**/*Test.js\"' npm run test",
"coverage:ci": "nyc --reporter=cobertura --exclude='\"src/test/**/*Test.js\"' npm run test && codecov",
"deploy": "rm -Rf ./deploy && npm run deploy:webpack && npm run deploy:s3",
"deploy": "rm -Rf deploy && npm run deploy:webpack && npm run deploy:s3",
"deploy:s3": "node deploy.js",
"deploy:webpack": "webpack --config src/webpack/deploy.webpack.config.js --progress --profile --colors --display-error-details --display-cached",
"lint": "sui-lint js",
"phoenix": "rm -Rf node_modules && rm -Rf package-lock.json && npm i",
"prepack": "npm run build",
"start": "webpack-dev-server --config src/webpack/dev.webpack.config.js",
"test": "sui-test server --pattern '\"src/test/**/*Test.js\"'",
"versiona": "node versiona.js"
Expand Down
4 changes: 3 additions & 1 deletion src/main/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
console.log('nothing here yet')
import {registerStub} from './service/registerStub'

export default registerStub
34 changes: 34 additions & 0 deletions src/main/service/handler/PostMessageHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export class PostMessageHandler {
handle(event = {}) {
const payload = this._toPayload(event.data)
payload &&
window.__tcfapi(
payload.command,
payload.version,
(retValue, success) => {
const returnMsg = {
__tcfapiReturn: {
returnValue: retValue,
success: success,
callId: payload.callId
}
}
if (typeof event.data === 'string') {
event.source.postMessage(JSON.stringify(returnMsg), '*')
} else {
event.source.postMessage(returnMsg, '*')
}
},
payload.parameter
)
}

_toPayload(message = {}) {
try {
const json = typeof message === 'string' ? JSON.parse(message) : message
return json?.__tcfapiCall
} catch (error) {
return undefined
}
}
}
33 changes: 33 additions & 0 deletions src/main/service/handler/TcfApiHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable standard/no-callback-literal */
export class TcfApiHandler {
constructor() {
this._queue = []
}

handle({command, version, callback, parameter}) {
switch (command) {
case PING_COMMAND: {
if (typeof callback === 'function') {
callback({
gdprApplies: true,
cmpLoaded: false,
cmpStatus: 'stub'
})
}
break
}
case PENDING_COMMAND: {
return this._queue
}
default: {
this._queue.push(() =>
window.__tcfapi(command, version, callback, parameter)
)
break
}
}
}
}

const PING_COMMAND = 'ping'
const PENDING_COMMAND = 'pending'
10 changes: 10 additions & 0 deletions src/main/service/registerIframeMessageHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {PostMessageHandler} from './handler/PostMessageHandler'

const postMessageHandler = new PostMessageHandler()
export const registerIframeMessageHandler = () => {
window.addEventListener(
'message',
event => postMessageHandler.handle(event),
false
)
}
14 changes: 14 additions & 0 deletions src/main/service/registerStub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {registerTcfApiLocator} from './registerTcfApiLocator'
import {registerIframeMessageHandler} from './registerIframeMessageHandler'
import {registerTcfApiHandler} from './registerTcfApiHandler'

export const registerStub = () => {
if (typeof window === 'undefined') {
return
}
if (!registerTcfApiLocator()) {
return
}
registerIframeMessageHandler()
registerTcfApiHandler()
}
7 changes: 7 additions & 0 deletions src/main/service/registerTcfApiHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {TcfApiHandler} from './handler/TcfApiHandler'

const tcfApiHandler = new TcfApiHandler()
export const registerTcfApiHandler = () => {
window.__tcfapi = (command, version, callback, parameter) =>
tcfApiHandler.handle({command, version, callback, parameter})
}
16 changes: 16 additions & 0 deletions src/main/service/registerTcfApiLocator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {waitUntil} from './waitUntil'

export const registerTcfApiLocator = () => {
if (window.frames[TCF_API_LOCATOR]) {
return false
}
waitUntil({condition: () => !!window.document.body}).then(() => {
const iframe = window.document.createElement('iframe')
iframe.style.cssText = 'display:none'
iframe.name = TCF_API_LOCATOR
window.document.body.appendChild(iframe)
})
return true
}

const TCF_API_LOCATOR = '__tcfapiLocator'
20 changes: 20 additions & 0 deletions src/main/service/waitUntil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const waitUntil = ({
condition,
timeout = 20000,
interval = 5,
timeoutMessage
}) =>
new Promise((resolve, reject) => {
const iid = setInterval(() => {
if (condition()) {
clearTimeout(tid)
clearInterval(iid)
resolve()
}
}, interval)
const tid = setTimeout(() => {
clearTimeout(tid)
clearInterval(iid)
reject(new Error(timeoutMessage))
}, timeout)
})
67 changes: 65 additions & 2 deletions src/test/indexTest.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,69 @@
import jsdom from 'jsdom-global'
import {expect} from 'chai'
import registerStub from '../main'
import {waitUntil} from '../main/service/waitUntil'

describe('boros tcf stub', () => {
it('should be tested', () => {
expect(true).to.be.true
beforeEach(() => {
jsdom(null, {runScripts: 'dangerously'})
window.postMessage = message => {
const event = new window.MessageEvent('message', {
data: message,
source: window
})
window.dispatchEvent(event)
}
})

it('should register the __tcfapiLocator iframe', () => {
registerStub()
return waitUntil({condition: () => !!window.frames.__tcfapiLocator})
})

it('should register the __tcfapi facade with pending command', () => {
registerStub()
let pending = window.__tcfapi('pending')
expect(pending.length).to.equal(0)

let pinged = false
window.__tcfapi('ping', 2, () => (pinged = true))
expect(pinged).to.be.true
pending = window.__tcfapi('pending')
expect(pending.length).to.equal(0)

window.__tcfapi('anyOtherCommand', 2, () => null)
pending = window.__tcfapi('pending')
expect(pending.length).to.equal(1)
})

it('should accept iframe communications', () => {
registerStub()

const callId = Math.random() + ''
const msg = {
__tcfapiCall: {
command: 'ping',
version: '2',
callId: callId
}
}

const responsePromise = new Promise((resolve, reject) =>
window.addEventListener('message', event => {
try {
const response = event.data.__tcfapiReturn
if (response) {
expect(response.callId).to.equal(callId)
expect(response.returnValue.cmpStatus).to.equal('stub')
resolve()
}
} catch (error) {
reject(error)
}
})
)

window.postMessage(msg, '*')
return responsePromise
})
})
2 changes: 1 addition & 1 deletion src/webpack/deploy.webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = [
{
devtool: distMinified ? false : 'inline-source-map',
entry: {
BorosTcfStub: './src/main/index.js'
BorosTcfStub: './src/webpack/standalone.index.js'
},
output: {
path: path.resolve(path.join(__dirname, '/../../', 'deploy')),
Expand Down
3 changes: 0 additions & 3 deletions src/webpack/dev.index.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/webpack/dev.webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const {CleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
mode: 'development',
entry: {
app: './src/webpack/dev.index.js'
app: './src/webpack/standalone.index.js'
},
devtool: 'inline-source-map',
plugins: [
Expand Down
3 changes: 3 additions & 0 deletions src/webpack/standalone.index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import registerStub from '../main'

registerStub()
1 change: 0 additions & 1 deletion versiona.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@ const versiona = require('versiona')
versiona({
repoOrg: 'scm-spain',
repoName: 'boros-tcf-stub',
publish: false,
masterCommand: env => `DEPLOY_ENV=${env} npm run deploy`
})

0 comments on commit 9300042

Please sign in to comment.