Skip to content
Merged
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"
]
}
62 changes: 62 additions & 0 deletions spec/src/modules/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe(`ConstructorIO - Autocomplete${bundledDescriptionSuffix}`, () => {
afterEach(() => {
delete global.CLIENT_VERSION;
delete window.CLIENT_VERSION;
delete window.cnstrc;
cleanup();

fetchSpy = null;
Expand Down Expand Up @@ -598,6 +599,67 @@ describe(`ConstructorIO - Autocomplete${bundledDescriptionSuffix}`, () => {
autocomplete.getAutocompleteResults(query);
});

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

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

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

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

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

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

it('Should not include window globals when useWindowParameters 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');
done();
});
});

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

Expand Down
62 changes: 62 additions & 0 deletions spec/src/modules/browse.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe(`ConstructorIO - Browse${bundledDescriptionSuffix}`, () => {
afterEach(() => {
delete global.CLIENT_VERSION;
delete window.CLIENT_VERSION;
delete window.cnstrc;
cleanup();

fetchSpy = null;
Expand Down Expand Up @@ -153,6 +154,67 @@ describe(`ConstructorIO - Browse${bundledDescriptionSuffix}`, () => {
});
});

it('Should include window global userId when useWindowParameters is true and options.userId is absent', (done) => {

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: The search.js tests include a "Should prefer options.userId over window global when useWindowParameters is true" priority test (around line 185), but browse.js, autocomplete.js, and recommendations.js do not have equivalent priority tests for userId, testCells, and segments. For consistency and to fully verify that explicit options take precedence over window globals in all modules, consider adding these priority tests across all four module test files — or at minimum document that the priority is exclusively tested in search.js.

window.cnstrc = { userId: 'window-user-id' };
const { browse } = new ConstructorIO({
apiKey: testApiKey,
useWindowParameters: true,
fetch: fetchSpy,
});

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

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

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

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

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

it('Should not include window globals when useWindowParameters 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');
done();
});
});

it('Should return a response with a valid filterName, filterValue and page', (done) => {
const page = 1;
const { browse } = new ConstructorIO({
Expand Down
62 changes: 62 additions & 0 deletions spec/src/modules/recommendations.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe(`ConstructorIO - Recommendations${bundledDescriptionSuffix}`, () => {
afterEach(() => {
delete global.CLIENT_VERSION;
delete window.CLIENT_VERSION;
delete window.cnstrc;
cleanup();

fetchSpy = null;
Expand Down Expand Up @@ -574,5 +575,66 @@ describe(`ConstructorIO - Recommendations${bundledDescriptionSuffix}`, () => {
)).to.eventually.be.rejectedWith(timeoutRejectionMessage);
});
}

it('Should include window global userId when useWindowParameters is true and options.userId is absent', (done) => {
window.cnstrc = { userId: 'window-user-id' };
const { recommendations } = new ConstructorIO({
apiKey: testApiKey,
useWindowParameters: 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');
done();
});
});

it('Should include window global testCells when useWindowParameters is true and options.testCells is absent', (done) => {
window.cnstrc = { testCells: { experiment: 'variation_a' } };
const { recommendations } = new ConstructorIO({
apiKey: testApiKey,
useWindowParameters: 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');
done();
});
});

it('Should include window global userSegments when useWindowParameters is true and options.segments is absent', (done) => {
window.cnstrc = { userSegments: ['vip', 'beta'] };
const { recommendations } = new ConstructorIO({
apiKey: testApiKey,
useWindowParameters: 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']);
done();
});
});

it('Should not include window globals when useWindowParameters 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');
done();
});
});
});
});
78 changes: 78 additions & 0 deletions spec/src/modules/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe(`ConstructorIO - Search${bundledDescriptionSuffix}`, () => {
afterEach(() => {
delete global.CLIENT_VERSION;
delete window.CLIENT_VERSION;
delete window.cnstrc;
cleanup();

fetchSpy = null;
Expand Down Expand Up @@ -150,6 +151,83 @@ describe(`ConstructorIO - Search${bundledDescriptionSuffix}`, () => {
});
});

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

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

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

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

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

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

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

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

it('Should not include window globals when useWindowParameters is false', (done) => {
window.cnstrc = { userId: 'window-user-id', testCells: { exp: 'var' }, userSegments: ['seg'] };
Comment thread
constructor-claude-bedrock[bot] marked this conversation as resolved.
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');
done();
});
});

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

delete window.CLIENT_VERSION;
delete global.CLIENT_VERSION;
delete window.cnstrc;
cleanup();

setTimeout(done, delayBetweenTests);
Expand Down Expand Up @@ -459,6 +460,50 @@ describe(`ConstructorIO - Tracker${bundledDescriptionSuffix}`, () => {

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

it('Should include window globals in tracking requests when useWindowParameters is true', (done) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important Issue: This test contradicts the PR description which says the tracker should NOT use window globals. See the comment on applyWindowParameterGetters for the full analysis.

Additionally, this test is missing a corresponding negative test ("Should NOT include window globals in tracking requests when useWindowParameters is false"), which all four other module test files have. Consistency here would verify the feature boundary behaves correctly across all modules.

window.cnstrc = { userId: 'window-user-id', testCells: { exp: 'var' }, userSegments: ['seg1', 'seg2'] };
const { tracker } = new ConstructorIO({
apiKey: testApiKey,
useWindowParameters: true,
fetch: fetchSpy,
...requestQueueOptions,
});

tracker.on('success', () => {
try {
const requestedUrlParams = helpers.extractUrlParamsFromFetch(fetchSpy);
expect(requestedUrlParams).to.have.property('ui').to.equal('window-user-id');
expect(requestedUrlParams).to.have.property('ef-exp').to.equal('var');
expect(requestedUrlParams).to.have.property('us').to.deep.equal(['seg1', 'seg2']);
done();
} catch (e) {
done(e);
}
});

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

it('Should prefer options.userId over window global in tracking requests when useWindowParameters is true', (done) => {
window.cnstrc = { userId: 'window-user-id' };
const instance = new ConstructorIO({
apiKey: testApiKey,
useWindowParameters: 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');
done();
});

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

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