Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"testdata",
"Bytespider",
"Timespans",
"googlequicksearchbox"
"googlequicksearchbox",
"cnstrc"
]
}
65 changes: 65 additions & 0 deletions spec/src/modules/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,71 @@ describe(`ConstructorIO - Autocomplete${bundledDescriptionSuffix}`, () => {
autocomplete.getAutocompleteResults(query);
});

it('Should include window global userId when trackWindowParameters is true and options.userId is absent', (done) => {
window.cnstrc = { userId: 'window-user-id' };
const { autocomplete } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

autocomplete.getAutocompleteResults(query).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ui').to.equal('window-user-id');
delete window.cnstrc;
done();
});
});

it('Should include window global testCells when trackWindowParameters is true and options.testCells is absent', (done) => {
window.cnstrc = { testCells: { experiment: 'variation_a' } };
const { autocomplete } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

autocomplete.getAutocompleteResults(query).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ef-experiment').to.equal('variation_a');
delete window.cnstrc;
done();
});
});

it('Should include window global userSegments when trackWindowParameters is true and options.segments is absent', (done) => {
window.cnstrc = { userSegments: ['vip', 'beta'] };
const { autocomplete } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

autocomplete.getAutocompleteResults(query).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('us').to.deep.equal(['vip', 'beta']);
delete window.cnstrc;
done();
});
});

it('Should not include window globals when trackWindowParameters is false', (done) => {
window.cnstrc = { userId: 'window-user-id', testCells: { exp: 'var' }, userSegments: ['seg'] };
const { autocomplete } = new ConstructorIO({
apiKey: testApiKey,
fetch: fetchSpy,
});

autocomplete.getAutocompleteResults(query).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.not.have.property('ui');
expect(requestedUrlParams).to.not.have.property('ef-exp');
expect(requestedUrlParams).to.not.have.property('us');
delete window.cnstrc;
done();
});
});

it('Should be rejected when invalid query is provided', () => {
const { autocomplete } = new ConstructorIO({ apiKey: testApiKey });

Expand Down
65 changes: 65 additions & 0 deletions spec/src/modules/browse.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,71 @@ describe(`ConstructorIO - Browse${bundledDescriptionSuffix}`, () => {
});
});

it('Should include window global userId when trackWindowParameters is true and options.userId is absent', (done) => {
window.cnstrc = { userId: 'window-user-id' };
const { browse } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

browse.getBrowseResults(filterName, filterValue).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ui').to.equal('window-user-id');
delete window.cnstrc;
done();
});
});

it('Should include window global testCells when trackWindowParameters is true and options.testCells is absent', (done) => {
window.cnstrc = { testCells: { experiment: 'variation_a' } };
const { browse } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

browse.getBrowseResults(filterName, filterValue).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ef-experiment').to.equal('variation_a');
delete window.cnstrc;
done();
});
});

it('Should include window global userSegments when trackWindowParameters is true and options.segments is absent', (done) => {
window.cnstrc = { userSegments: ['vip', 'beta'] };
const { browse } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

browse.getBrowseResults(filterName, filterValue).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('us').to.deep.equal(['vip', 'beta']);
delete window.cnstrc;
done();
});
});

it('Should not include window globals when trackWindowParameters is false', (done) => {
window.cnstrc = { userId: 'window-user-id', testCells: { exp: 'var' }, userSegments: ['seg'] };
const { browse } = new ConstructorIO({
apiKey: testApiKey,
fetch: fetchSpy,
});

browse.getBrowseResults(filterName, filterValue).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.not.have.property('ui');
expect(requestedUrlParams).to.not.have.property('ef-exp');
expect(requestedUrlParams).to.not.have.property('us');
delete window.cnstrc;
done();
});
});

it('Should return a response with a valid filterName, filterValue and page', (done) => {
const page = 1;
const { browse } = new ConstructorIO({
Expand Down
65 changes: 65 additions & 0 deletions spec/src/modules/recommendations.js
Original file line number Diff line number Diff line change
Expand Up @@ -574,5 +574,70 @@ describe(`ConstructorIO - Recommendations${bundledDescriptionSuffix}`, () => {
)).to.eventually.be.rejectedWith(timeoutRejectionMessage);
});
}

it('Should include window global userId when trackWindowParameters is true and options.userId is absent', (done) => {
window.cnstrc = { userId: 'window-user-id' };
const { recommendations } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

recommendations.getRecommendations(podId, { itemIds: itemId }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ui').to.equal('window-user-id');
delete window.cnstrc;
done();
});
});

it('Should include window global testCells when trackWindowParameters is true and options.testCells is absent', (done) => {
window.cnstrc = { testCells: { experiment: 'variation_a' } };
const { recommendations } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

recommendations.getRecommendations(podId, { itemIds: itemId }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ef-experiment').to.equal('variation_a');
delete window.cnstrc;
done();
});
});

it('Should include window global userSegments when trackWindowParameters is true and options.segments is absent', (done) => {
window.cnstrc = { userSegments: ['vip', 'beta'] };
const { recommendations } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

recommendations.getRecommendations(podId, { itemIds: itemId }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('us').to.deep.equal(['vip', 'beta']);
delete window.cnstrc;
done();
});
});

it('Should not include window globals when trackWindowParameters is false', (done) => {
window.cnstrc = { userId: 'window-user-id', testCells: { exp: 'var' }, userSegments: ['seg'] };
const { recommendations } = new ConstructorIO({
apiKey: testApiKey,
fetch: fetchSpy,
});

recommendations.getRecommendations(podId, { itemIds: itemId }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.not.have.property('ui');
expect(requestedUrlParams).to.not.have.property('ef-exp');
expect(requestedUrlParams).to.not.have.property('us');
delete window.cnstrc;
done();
});
});
});
});
82 changes: 82 additions & 0 deletions spec/src/modules/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,88 @@ describe(`ConstructorIO - Search${bundledDescriptionSuffix}`, () => {
});
});

it('Should include window global userId when trackWindowParameters is true and options.userId is absent', (done) => {
window.cnstrc = { userId: 'window-user-id' };
const { search } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

search.getSearchResults(query, { section }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ui').to.equal('window-user-id');
delete window.cnstrc;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several tests set window.cnstrc and clean it up with delete window.cnstrc inside the .then() callback. If an assertion throws first (or the promise rejects), the global leaks into subsequent tests. Can we move cleanup to afterEach?

done();
});
});

it('Should include window global testCells when trackWindowParameters is true and options.testCells is absent', (done) => {
window.cnstrc = { testCells: { experiment: 'variation_a' } };
const { search } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

search.getSearchResults(query, { section }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ef-experiment').to.equal('variation_a');
delete window.cnstrc;
done();
});
});

it('Should include window global userSegments when trackWindowParameters is true and options.segments is absent', (done) => {
window.cnstrc = { userSegments: ['vip', 'beta'] };
const { search } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
});

search.getSearchResults(query, { section }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('us').to.deep.equal(['vip', 'beta']);
delete window.cnstrc;
done();
});
});

it('Should prefer options.userId over window global when trackWindowParameters is true', (done) => {
window.cnstrc = { userId: 'window-user-id' };
const { search } = new ConstructorIO({
apiKey: testApiKey,
userId: 'options-user-id',
trackWindowParameters: true,
fetch: fetchSpy,
});

search.getSearchResults(query, { section }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ui').to.equal('options-user-id');
delete window.cnstrc;
done();
});
});

it('Should not include window globals when trackWindowParameters is false', (done) => {
window.cnstrc = { userId: 'window-user-id', testCells: { exp: 'var' }, userSegments: ['seg'] };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Several test cases set window.cnstrc and then clean it up inside the .then() callback using delete window.cnstrc. If the then() callback throws before the delete (e.g., a failed assertion), or if the Promise rejects, window.cnstrc will leak into subsequent tests.

Consider using afterEach to clean up window globals, similar to the pattern already used in spec/src/utils/helpers.js for the applyWindowParameterGetters tests:

afterEach(() => { delete window.cnstrc; });

This pattern appears in all four module test files (search.js, browse.js, autocomplete.js, recommendations.js) and tracker.js.

const { search } = new ConstructorIO({
apiKey: testApiKey,
fetch: fetchSpy,
});

search.getSearchResults(query, { section }).then(() => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.not.have.property('ui');
expect(requestedUrlParams).to.not.have.property('ef-exp');
expect(requestedUrlParams).to.not.have.property('us');
delete window.cnstrc;
done();
});
});

it('Should return a response with a valid query, section, and offset', (done) => {
const offset = 1;
const { search } = new ConstructorIO({
Expand Down
42 changes: 42 additions & 0 deletions spec/src/modules/tracker.js
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,48 @@ describe(`ConstructorIO - Tracker${bundledDescriptionSuffix}`, () => {

expect(tracker.trackSessionStart()).to.equal(true);
});

it('Should not include window globals in tracking requests even when trackWindowParameters is true', (done) => {
window.cnstrc = { userId: 'window-user-id', testCells: { exp: 'var' }, userSegments: ['seg'] };
const { tracker } = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
...requestQueueOptions,
});

tracker.on('success', () => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.not.have.property('ui');
expect(requestedUrlParams).to.not.have.property('ef-exp');
expect(requestedUrlParams).to.not.have.property('us');
delete window.cnstrc;
done();
});

expect(tracker.trackSessionStart()).to.equal(true);
});

it('Should include userId set via setClientOptions in tracking requests', (done) => {
window.cnstrc = { userId: 'window-user-id' };
const instance = new ConstructorIO({
apiKey: testApiKey,
trackWindowParameters: true,
fetch: fetchSpy,
...requestQueueOptions,
});

instance.setClientOptions({ userId: 'explicit-user-id' });

instance.tracker.on('success', () => {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ui').to.equal('explicit-user-id');
delete window.cnstrc;
done();
});

expect(instance.tracker.trackSessionStart()).to.equal(true);
});
});

describe('trackInputFocus', () => {
Expand Down
Loading
Loading