-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin' into PPLT_2182_add_type_check
- Loading branch information
Showing
8 changed files
with
353 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
const { Undefined } = require('./validations'); | ||
|
||
class Cache { | ||
static cache = {}; | ||
|
||
static capabilities = 'capabilities'; | ||
static sessionCapabilities = 'session_capabilites'; | ||
static commandExecutorUrl = 'command_executor_url'; | ||
|
||
static lastTime = Date.now(); | ||
static timeout = 5 * 60 * 1000; | ||
|
||
static async withCache(store, key, func, cacheExceptions = false) { | ||
this.maintain(); | ||
if (Undefined(this.cache[store])) this.cache[store] = {}; | ||
|
||
store = this.cache[store]; | ||
if (store[key]) { | ||
if (store[key].success) { | ||
return store[key].val; | ||
} else { | ||
throw store[key].val; | ||
} | ||
} | ||
|
||
const obj = { success: false, val: null, time: Date.now() }; | ||
try { | ||
obj.val = await func(); | ||
obj.success = true; | ||
} catch (e) { | ||
obj.val = e; | ||
} | ||
|
||
// We seem to have correct coverage for both flows but nyc is marking it as missing | ||
// branch coverage anyway | ||
/* istanbul ignore next */ | ||
if (obj.success || cacheExceptions) { | ||
store[key] = obj; | ||
} | ||
|
||
if (!obj.success) throw obj.val; | ||
return obj.val; | ||
} | ||
|
||
static maintain() { | ||
if (this.lastTime + this.timeout > Date.now()) return; | ||
|
||
for (const [, store] of Object.entries(this.cache)) { | ||
for (const [key, item] of Object.entries(store)) { | ||
if (item.time + this.timeout < Date.now()) { | ||
delete store[key]; | ||
} | ||
} | ||
} | ||
this.lastTime = Date.now(); | ||
} | ||
|
||
static reset() { | ||
this.cache = {}; | ||
} | ||
} | ||
|
||
module.exports = { | ||
Cache | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// const utils = require('@percy/sdk-utils'); | ||
const { Cache } = require('./cache'); | ||
const { RequestInterceptor } = require('node-request-interceptor'); | ||
const withDefaultInterceptors = require('node-request-interceptor/lib/presets/default'); | ||
|
||
class DriverMetadata { | ||
constructor(driver) { | ||
this.driver = driver; | ||
this.sessionId = null; | ||
if (this.driver.constructor.name === 'Browser') { | ||
this.type = 'wdio'; | ||
} else { | ||
this.type = 'wd'; | ||
} | ||
} | ||
|
||
async getSessionId() { | ||
if (!this.sessionId) { | ||
if (this.type === 'wdio') this.sessionId = await this.driver.sessionId; | ||
if (this.type === 'wd') this.sessionId = await (await this.driver.getSession()).getId(); | ||
} | ||
return this.sessionId; | ||
} | ||
|
||
async getCapabilities() { | ||
return await Cache.withCache(Cache.capabilities, await this.getSessionId(), async () => { | ||
if (this.type === 'wdio') { | ||
return await this.driver.capabilities; | ||
} else { | ||
const session = await this.driver.getSession(); | ||
const capabilities = Object.fromEntries(session.getCapabilities().map_); | ||
return capabilities; | ||
} | ||
}); | ||
} | ||
|
||
async getCommandExecutorUrl() { | ||
return await Cache.withCache(Cache.commandExecutorUrl, await this.getSessionId(), async () => { | ||
if (this.type === 'wdio') { | ||
return `${this.driver.options.protocol}://${this.driver.options.hostname}${this.driver.options.path}`; | ||
} else { | ||
// To intercept request from driver. used to get remote server url | ||
const interceptor = new RequestInterceptor(withDefaultInterceptors.default); | ||
let commandExecutorUrl = ''; | ||
interceptor.use((req) => { | ||
const url = req.url.href; | ||
commandExecutorUrl = url.split('/session')[0]; | ||
}); | ||
// making a call so we can intercept commandExecutorUrl | ||
await this.driver.getCurrentUrl(); | ||
// To stop intercepting request | ||
interceptor.restore(); | ||
return commandExecutorUrl; | ||
} | ||
}); | ||
} | ||
} | ||
|
||
module.exports = { | ||
DriverMetadata | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import { Cache } from '../cache.js' | ||
|
||
describe('Cache', () => { | ||
const store = 'abc'; | ||
const key = 'key'; | ||
|
||
beforeEach(async () => { | ||
Cache.reset(); | ||
}); | ||
|
||
describe('withCache', () => { | ||
it('caches response', async () => { | ||
const expectedVal = 123; | ||
const func = jasmine.createSpy('func').and.returnValue(expectedVal); | ||
let val = await Cache.withCache(store, key, func); | ||
expect(func.calls.count()).toEqual(1); | ||
expect(val).toEqual(expectedVal); | ||
|
||
val = await Cache.withCache(store, key, func); | ||
expect(func.calls.count()).toEqual(1); | ||
expect(val).toEqual(expectedVal); | ||
}); | ||
|
||
describe('with different key but same store', () => { | ||
it('calls func again and caches it', async () => { | ||
const expectedVal = 123; | ||
const func = jasmine.createSpy('func').and.returnValue(expectedVal); | ||
const key2 = 'key2'; | ||
|
||
let val = await Cache.withCache(store, key, func); | ||
expect(func.calls.count()).toEqual(1); | ||
expect(val).toEqual(expectedVal); | ||
|
||
val = await Cache.withCache(store, key2, func); | ||
expect(func.calls.count()).toEqual(2); | ||
expect(val).toEqual(expectedVal); | ||
|
||
// test both cache | ||
val = await Cache.withCache(store, key, func); | ||
expect(func.calls.count()).toEqual(2); // does not increment | ||
expect(val).toEqual(expectedVal); | ||
|
||
val = await Cache.withCache(store, key2, func); | ||
expect(func.calls.count()).toEqual(2); // does not increment | ||
expect(val).toEqual(expectedVal); | ||
}); | ||
}); | ||
|
||
describe('with different store but same key', () => { | ||
it('calls func again and caches it', async () => { | ||
const expectedVal = 123; | ||
const func = jasmine.createSpy('func').and.returnValue(expectedVal); | ||
const store2 = 'store2'; | ||
|
||
let val = await Cache.withCache(store, key, func); | ||
expect(func.calls.count()).toEqual(1); | ||
expect(val).toEqual(expectedVal); | ||
|
||
val = await Cache.withCache(store2, key, func); | ||
expect(func.calls.count()).toEqual(2); | ||
expect(val).toEqual(expectedVal); | ||
|
||
// test both cache | ||
val = await Cache.withCache(store, key, func); | ||
expect(func.calls.count()).toEqual(2); // does not increment | ||
expect(val).toEqual(expectedVal); | ||
|
||
val = await Cache.withCache(store2, key, func); | ||
expect(func.calls.count()).toEqual(2); // does not increment | ||
expect(val).toEqual(expectedVal); | ||
}); | ||
}); | ||
|
||
describe('with cacheExceptions', () => { | ||
it('caches exceptions', async () => { | ||
const expectedError = new Error('Some error'); | ||
const func = jasmine.createSpy('func').and.throwError(expectedError); | ||
|
||
let actualError = null; | ||
try { | ||
await Cache.withCache(store, key, func, true); | ||
} catch (e) { | ||
actualError = e; | ||
} | ||
|
||
expect(func.calls.count()).toEqual(1); | ||
expect(actualError).toEqual(expectedError); | ||
|
||
try { | ||
await Cache.withCache(store, key, func, true); | ||
} catch (e) { | ||
actualError = e; | ||
} | ||
|
||
expect(func.calls.count()).toEqual(1); | ||
expect(actualError).toEqual(expectedError); | ||
}); | ||
}); | ||
|
||
describe('with expired cache', () => { | ||
const originalCacheTimeout = Cache.timeout; | ||
beforeAll(() => { | ||
Cache.timeout = 7; // 7ms | ||
}); | ||
|
||
afterAll(() => { | ||
Cache.timeout = originalCacheTimeout; | ||
}); | ||
|
||
it('calls func again and caches it', async () => { | ||
const expectedVal = 123; | ||
const func = jasmine.createSpy('func').and.returnValue(expectedVal); | ||
|
||
let val = await Cache.withCache(store, key, func); | ||
expect(func.calls.count()).toEqual(1); | ||
expect(val).toEqual(expectedVal); | ||
|
||
// wait for expiry | ||
await new Promise((resolve) => setTimeout(resolve, 10)); | ||
|
||
// create a test entry that should not get deleted | ||
Cache.cache.random_store = {}; | ||
Cache.cache.random_store.some_new_key = { val: 1, time: Date.now(), success: true }; | ||
|
||
// test expired cache | ||
val = await Cache.withCache(store, key, func); | ||
expect(func.calls.count()).toEqual(2); | ||
expect(val).toEqual(expectedVal); | ||
|
||
// Not deleted | ||
expect(Cache.cache.random_store.some_new_key).toBeTruthy(); | ||
}); | ||
|
||
it('it invalidates all expired keys on any call', async () => { | ||
const expectedVal = 123; | ||
const func = jasmine.createSpy('func').and.returnValue(expectedVal); | ||
const key2 = 'key2'; | ||
const store2 = 'store2'; | ||
|
||
await Cache.withCache(store, key, func); | ||
await Cache.withCache(store, key2, func); | ||
await Cache.withCache(store2, key, func); | ||
|
||
// wait for expiry | ||
await new Promise((resolve) => setTimeout(resolve, 10)); | ||
|
||
// test expired cache | ||
await Cache.withCache(store, key, func); | ||
expect(func.calls.count()).toEqual(4); | ||
|
||
// check internal to avoid calling via withCache | ||
expect(Cache.cache[store2][key]).toBeUndefined(); | ||
expect(Cache.cache[store2][key2]).toBeUndefined(); | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.