diff --git a/cspell.json b/cspell.json index 65f1a318..2ee44e04 100644 --- a/cspell.json +++ b/cspell.json @@ -47,6 +47,5 @@ "rerank", "atcs", "testdata", - "Bytespider" ] } diff --git a/spec/src/utils/humanity-check.js b/spec/src/utils/humanity-check.js index 9390a0e8..569eb1fa 100644 --- a/spec/src/utils/humanity-check.js +++ b/spec/src/utils/humanity-check.js @@ -1,8 +1,4 @@ -/* eslint-disable - no-underscore-dangle, - no-restricted-properties, - import/no-unresolved -*/ +/* eslint-disable import/no-unresolved */ const dotenv = require('dotenv'); const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); @@ -19,7 +15,7 @@ const bundled = process.env.BUNDLED === 'true'; describe('ConstructorIO - Utils - Humanity Check', () => { // Don't run tests in bundle context, as these tests are for library internals if (!bundled) { - describe('constructor', () => { + describe('isHuman', () => { const storageKey = '_constructorio_is_human'; let cleanup; @@ -39,89 +35,60 @@ describe('ConstructorIO - Utils - Humanity Check', () => { it('Should not have isHuman flag set on initial instantiation', () => { const humanity = new HumanityCheck(); - expect(humanity.hasPerformedHumanEvent).to.equal(false); + expect(humanity.isHuman()).to.equal(false); expect(store.session.get(storageKey)).to.equal(null); }); it('Should have isHuman flag set if human-like actions are detected', () => { const humanity = new HumanityCheck(); - expect(humanity.hasPerformedHumanEvent).to.equal(false); + expect(humanity.isHuman()).to.equal(false); helpers.triggerResize(); - expect(humanity.hasPerformedHumanEvent).to.equal(true); + expect(humanity.isHuman()).to.equal(true); expect(store.session.get(storageKey)).to.equal(true); }); - }); + it('Should have isHuman flag set if session variable is set', () => { + const humanity = new HumanityCheck(); + + expect(humanity.isHuman()).to.equal(false); + store.session.set(storageKey, true); + expect(humanity.isHuman()).to.equal(true); + expect(store.session.get(storageKey)).to.equal(true); + }); + }); describe('isBot', () => { const storageKey = '_constructorio_is_human'; let cleanup; - let defaultAgent; beforeEach(() => { global.CLIENT_VERSION = 'cio-mocha'; - cleanup = jsdom(); - defaultAgent = window.navigator.userAgent; + cleanup = jsdom(); + window.navigator.webdriver = true; }); afterEach(() => { delete global.CLIENT_VERSION; - delete window.navigator.webdriver; - window.navigator.__defineGetter__('userAgent', () => defaultAgent); cleanup(); helpers.clearStorage(); }); - it('Should return true if it detects a webdriver', () => { - window.navigator.webdriver = true; - const humanity = new HumanityCheck(); - - expect(humanity.isBot()).to.equal(true); - expect(store.session.get(storageKey)).to.equal(null); - }); - - it('Should return true if it detects a webdriver and the session variable is set via human-like action', () => { - window.navigator.webdriver = true; - const humanity = new HumanityCheck(); - - expect(humanity.isBot()).to.equal(true); - helpers.triggerResize(); - expect(humanity.isBot()).to.equal(true); - expect(store.session.get(storageKey)).to.equal(true); - }); - - it('Should return true if it detects a bot user agent', () => { - window.navigator.__defineGetter__('userAgent', () => 'Mozilla/5.0 (Linux; Android 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; Bytespider; spider-feedback@bytedance.com)'); + it('Should have isBot flag set on initial instantiation', () => { const humanity = new HumanityCheck(); expect(humanity.isBot()).to.equal(true); expect(store.session.get(storageKey)).to.equal(null); }); - it('Should return true if it detects a bot user agent and the session variable is set via human-like action', () => { - window.navigator.__defineGetter__('userAgent', () => 'Mozilla/5.0 (Linux; Android 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; Bytespider; spider-feedback@bytedance.com)'); - const humanity = new HumanityCheck(); - - expect(humanity.isBot()).to.equal(true); - helpers.triggerResize(); - expect(humanity.isBot()).to.equal(true); - expect(store.session.get(storageKey)).to.equal(true); - }); - - it('Should return true if it does not detect the user is a bot user nor webdriver and the session variable has not been set yet', () => { - const humanity = new HumanityCheck(); - - expect(humanity.isBot()).to.equal(true); - }); - - it('Should return false if it does not detect the user is a bot/webdriver and the session variable is set via human-like action', () => { + it('Should have isBot flag set to false if session variable is set', () => { const humanity = new HumanityCheck(); expect(humanity.isBot()).to.equal(true); - helpers.triggerResize(); + store.session.set(storageKey, true); expect(humanity.isBot()).to.equal(false); + expect(store.session.get(storageKey)).to.equal(true); }); }); } diff --git a/spec/src/utils/request-queue.js b/spec/src/utils/request-queue.js index 53f6555b..6bd8451c 100644 --- a/spec/src/utils/request-queue.js +++ b/spec/src/utils/request-queue.js @@ -31,7 +31,6 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { this.timeout(3000); const storageKey = '_constructorio_requests'; - const humanityStorageKey = '_constructorio_is_human'; const waitInterval = 2000; let requestQueueOptions = {}; const piiExamples = [ @@ -180,8 +179,7 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { helpers.clearStorage(); }); - it('Should add url requests to the queue if the user is human', async () => { - store.session.set(humanityStorageKey, true); + it('Should add url requests to the queue', () => { const requests = new RequestQueue(requestQueueOptions); requests.queue('https://ac.cnstrc.com/behavior?action=session_start'); @@ -193,8 +191,7 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { expect(store.local.get(storageKey)).to.be.an('array').length(3); }); - it('Should add object requests to the queue - POST with body if the user is human', () => { - store.session.set(humanityStorageKey, true); + it('Should add object requests to the queue - POST with body', () => { const requests = new RequestQueue(requestQueueOptions); requests.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'session_start' }); @@ -221,7 +218,6 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { }); it('Should obfuscate requests if PII is detected', () => { - store.session.set(humanityStorageKey, true); // Enabled for the test to run const requests = new RequestQueue(requestQueueOptions); const allExamples = []; @@ -256,7 +252,6 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { }); it('Should not obfuscate requests if no PII is detected', () => { - store.session.set(humanityStorageKey, true); // Enabled for the test to run const requests = new RequestQueue(requestQueueOptions); notPiiExamples.forEach((query) => { @@ -348,13 +343,12 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { it('Should send all url tracking requests if queue is populated and user is human', (done) => { const requests = new RequestQueue(requestQueueOptions); - helpers.triggerResize(); // Human-like action - requests.queue('https://ac.cnstrc.com/behavior?action=session_start'); requests.queue('https://ac.cnstrc.com/behavior?action=focus'); requests.queue('https://ac.cnstrc.com/behavior?action=magic_number_three'); expect(RequestQueue.get()).to.be.an('array').length(3); + helpers.triggerResize(); requests.send(); setTimeout(() => { @@ -365,7 +359,6 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { }); it('Should send all object tracking requests if queue is populated and user is human - POST with body', (done) => { - store.session.set(humanityStorageKey, true); const requests = new RequestQueue(requestQueueOptions); requests.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'session_start' }); @@ -383,26 +376,14 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { }, waitInterval); }); - it('Should not send tracking requests if queue was populated and user is not human', (done) => { + it('Should not send tracking requests if queue is populated and user is not human', (done) => { const requests = new RequestQueue(requestQueueOptions); - store.local.set(storageKey, [ - { - url: 'https://ac.cnstrc.com/behavior?action=session_start', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=focus', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=magic_number_three', - method: 'GET', - }, - ]); + requests.queue('https://ac.cnstrc.com/behavior?action=session_start'); + requests.queue('https://ac.cnstrc.com/behavior?action=focus'); + requests.queue('https://ac.cnstrc.com/behavior?action=magic_number_three'); expect(RequestQueue.get()).to.be.an('array').length(3); - requests.send(); setTimeout(() => { @@ -414,20 +395,9 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { it('Should not send tracking requests if queue is populated and user is human and page is unloading', (done) => { const requests = new RequestQueue(requestQueueOptions); - store.local.set(storageKey, [ - { - url: 'https://ac.cnstrc.com/behavior?action=session_start', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=focus', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=magic_number_three', - method: 'GET', - }, - ]); + requests.queue('https://ac.cnstrc.com/behavior?action=session_start'); + requests.queue('https://ac.cnstrc.com/behavior?action=focus'); + requests.queue('https://ac.cnstrc.com/behavior?action=magic_number_three'); expect(RequestQueue.get()).to.be.an('array').length(3); helpers.triggerResize(); @@ -443,20 +413,9 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { it('Should not send tracking requests if queue is populated and user is human and page is unloading and send was called before unload', (done) => { const requests = new RequestQueue(requestQueueOptions); - store.local.set(storageKey, [ - { - url: 'https://ac.cnstrc.com/behavior?action=session_start', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=focus', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=magic_number_three', - method: 'GET', - }, - ]); + requests.queue('https://ac.cnstrc.com/behavior?action=session_start'); + requests.queue('https://ac.cnstrc.com/behavior?action=focus'); + requests.queue('https://ac.cnstrc.com/behavior?action=magic_number_three'); expect(RequestQueue.get()).to.be.an('array').length(3); helpers.triggerResize(); @@ -769,28 +728,11 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { const sendSpy1 = sinon.spy(requests1, 'send'); const sendSpy2 = sinon.spy(requests2, 'send'); - store.local.set(storageKey, [ - { - url: 'https://ac.cnstrc.com/behavior?action=session_start', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=focus', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=magic_number_three', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=magic_number_four', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=magic_number_five', - method: 'GET', - }, - ]); + requests1.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_one' }); + requests1.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_two' }); + requests1.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_three' }); + requests1.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_four' }); + requests1.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_five' }); helpers.triggerResize(); requests1.send(); @@ -812,28 +754,11 @@ describe('ConstructorIO - Utils - Request Queue', function utilsRequestQueue() { const sendSpy1 = sinon.spy(requests1, 'send'); const sendSpy2 = sinon.spy(requests2, 'send'); - store.local.set(storageKey, [ - { - url: 'https://ac.cnstrc.com/behavior?action=session_start', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=focus', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=magic_number_three', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=magic_number_four', - method: 'GET', - }, - { - url: 'https://ac.cnstrc.com/behavior?action=magic_number_five', - method: 'GET', - }, - ]); + requests1.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_one' }); + requests2.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_two' }); + requests1.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_three' }); + requests2.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_four' }); + requests1.queue('https://ac.cnstrc.com/behavior', 'POST', { action: 'number_five' }); helpers.triggerResize(); requests1.send(); diff --git a/src/utils/humanity-check.js b/src/utils/humanity-check.js index 43f0d70d..ca482abe 100644 --- a/src/utils/humanity-check.js +++ b/src/utils/humanity-check.js @@ -18,12 +18,11 @@ const humanEvents = [ class HumanityCheck { constructor() { - // Check if a human event has been performed in the past - this.hasPerformedHumanEvent = !!store.session.get(storageKey) || false; + this.isHumanBoolean = this.getIsHumanFromSessionStorage(); - // Humanity proved, remove handlers + // Humanity proved, remove handlers to prove humanity const remove = () => { - this.hasPerformedHumanEvent = true; + this.isHumanBoolean = true; store.session.set(storageKey, true); humanEvents.forEach((eventType) => { @@ -32,33 +31,32 @@ class HumanityCheck { }; // Add handlers to prove humanity - if (!this.hasPerformedHumanEvent) { + if (!this.isHumanBoolean) { humanEvents.forEach((eventType) => { helpers.addEventListener(eventType, remove, true); }); } } - // Return boolean indicating if user is a bot - // ...if it has a bot-like useragent - // ...or uses webdriver - // ...or has not performed a human event - isBot() { - const { userAgent, webdriver } = helpers.getNavigator(); - const botRegex = new RegExp(`(${botList.join('|')})`); + getIsHumanFromSessionStorage() { + return !!store.session.get(storageKey) || false; + } - // Always check the user agent and webdriver fields first to determine if the user is a bot - if (Boolean(userAgent.match(botRegex)) || Boolean(webdriver)) { - return true; - } + // Return boolean indicating if is human + isHuman() { + return this.isHumanBoolean || !!store.session.get(storageKey); + } - // If the user hasn't performed a human event, it indicates it is a bot - if (!this.hasPerformedHumanEvent) { - return true; + // Return boolean indicating if useragent matches botlist + isBot() { + if (this.getIsHumanFromSessionStorage()) { + return false; } - // Otherwise, it is a human - return false; + const { userAgent, webdriver } = helpers.getNavigator(); + const botRegex = new RegExp(`(${botList.join('|')})`); + + return Boolean(userAgent.match(botRegex)) || Boolean(webdriver); } } diff --git a/src/utils/request-queue.js b/src/utils/request-queue.js index dc547ef3..02146c9a 100644 --- a/src/utils/request-queue.js +++ b/src/utils/request-queue.js @@ -54,7 +54,7 @@ class RequestQueue { if ( // Consider user "human" if no DOM context is available - (!helpers.canUseDOM() || !this.humanity.isBot()) + (!helpers.canUseDOM() || this.humanity.isHuman()) && !this.requestPending && !this.pageUnloading && queue.length