diff --git a/.github/workflows/check-version-bump.yml b/.github/workflows/check-version-bump.yml index 8e71000..6066930 100644 --- a/.github/workflows/check-version-bump.yml +++ b/.github/workflows/check-version-bump.yml @@ -74,9 +74,9 @@ jobs: echo "::error::Version bump required: package.json version ($PKG_VERSION) equals latest tag ($LATEST_TAG). Please bump the version in package.json." exit 1 fi - CHANGELOG_VERSION=$(sed -nE 's/^## \[v?([0-9]+\.[0-9]+\.[0-9]+).*/\1/p' CHANGELOG.md | head -1) + CHANGELOG_VERSION=$(sed -nE 's/^(## \[v?|### Version: )([0-9]+\.[0-9]+\.[0-9]+).*/\2/p' CHANGELOG.md | head -1) if [ -z "$CHANGELOG_VERSION" ]; then - echo "::error::Could not find a version entry in CHANGELOG.md (expected line like '## [v1.0.0](...)')." + echo "::error::Could not find a version entry in CHANGELOG.md (expected '## [v1.0.0](...)' or '### Version: 1.0.0')." exit 1 fi if [ "$CHANGELOG_VERSION" != "$PKG_VERSION" ]; then diff --git a/CHANGELOG.md b/CHANGELOG.md index d9ad64b..a78a0d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### Version: 5.3.0 +#### Date: May-18-2026 +Enhancement: Entry variants support an optional branch name as the second argument to `variants()` on `Entry` and `Entries`. When provided, the branch is sent as the `branch` request header together with `x-cs-variant-uid`. Existing `variants(uid)` and `variants(uids)` calls remain backward compatible. Added unit and API tests for variant + branch requests. + ### Version: 5.2.0 #### Date: Apr-09-2026 Enhancement: `ContentTypeQuery` extends `BaseQuery` so `stack.contentType()` supports `paginate`, `skip`, `limit`, and related query helpers without mutating `_queryParams`; `includeGlobalFieldSchema()` and `find()` stay backward compatible. Expanded unit tests for `ContentTypeQuery`. diff --git a/package-lock.json b/package-lock.json index d310abe..4542320 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@contentstack/delivery-sdk", - "version": "5.2.0", + "version": "5.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@contentstack/delivery-sdk", - "version": "5.2.0", + "version": "5.3.0", "license": "MIT", "dependencies": { "@contentstack/core": "^1.3.11", diff --git a/package.json b/package.json index 1973471..671edb2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/delivery-sdk", - "version": "5.2.0", + "version": "5.3.0", "type": "module", "license": "MIT", "engines": { diff --git a/src/common/utils.ts b/src/common/utils.ts index 24d4d0f..ca86d9b 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -37,3 +37,24 @@ export function encodeQueryParams(params: params): params { return encodedParams; } + +/** + * Builds request headers for entry variant requests. + * @param variants - Comma-separated variant UID(s) + * @param branch - Optional branch name to scope the variant request + */ +export function buildVariantRequestHeaders( + variants: string, + branch?: string +): Record | undefined { + const headers: Record = {}; + + if (variants) { + headers['x-cs-variant-uid'] = variants; + } + if (branch) { + headers.branch = branch; + } + + return Object.keys(headers).length > 0 ? headers : undefined; +} diff --git a/src/entries/entries.ts b/src/entries/entries.ts index 7eda606..ef599eb 100644 --- a/src/entries/entries.ts +++ b/src/entries/entries.ts @@ -2,7 +2,7 @@ import { AxiosInstance, getData } from '@contentstack/core'; import { Query } from '../query'; import { BaseQuery } from '../query'; import { FindResponse } from '../common/types'; -import { encodeQueryParams } from '../common/utils'; +import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils'; import { ErrorMessages } from '../common/error-messages'; export class Entries extends BaseQuery { @@ -14,6 +14,7 @@ export class Entries extends BaseQuery { this._contentTypeUid = contentTypeUid; this._urlPath = `/content_types/${this._contentTypeUid}/entries`; this._variants = ''; + this._variantsBranch = ''; } /** @@ -252,28 +253,36 @@ export class Entries extends BaseQuery { * const query = stack.contentType("contentTypeUid").entry().query(); */ query(queryObj?: { [key: string]: any }) { - if (queryObj) return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, queryObj); + if (queryObj) { + return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, this._variantsBranch, queryObj); + } - return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid); + return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, this._variantsBranch); } /** * @method variants * @memberof Entries - * @description The variant header will be added to axios client + * @description The variant header will be added to axios client. Branch is optional. + * @param {string | string[]} variants - Variant UID or UIDs + * @param {string} [branchName] - Optional branch name sent as the `branch` header * @returns {Entries} * @example * import contentstack from '@contentstack/delivery-sdk' * * const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); * const result = await stack.contentType('abc').entry().variants('xyz').find(); + * const resultWithBranch = await stack.contentType('abc').entry().variants('xyz', 'branch_name').find(); */ - variants(variants: string | string[]): Entries { + variants(variants: string | string[], branchName?: string): Entries { if (Array.isArray(variants) && variants.length > 0) { this._variants = variants.join(','); } else if (typeof variants == 'string' && variants.length > 0) { this._variants = variants; } + if (typeof branchName === 'string' && branchName.length > 0) { + this._variantsBranch = branchName; + } return this; } @@ -320,10 +329,11 @@ export class Entries extends BaseQuery { contentTypeUid: this._contentTypeUid }; - if (this._variants) { + const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch); + if (variantHeaders) { getRequestOptions.headers = { ...getRequestOptions.headers, - 'x-cs-variant-uid': this._variants + ...variantHeaders }; } const response = await getData(this._client, this._urlPath, getRequestOptions); diff --git a/src/entries/entry.ts b/src/entries/entry.ts index a7b6e5c..c3e47c3 100644 --- a/src/entries/entry.ts +++ b/src/entries/entry.ts @@ -1,5 +1,6 @@ import { AxiosInstance, getData } from '@contentstack/core'; import { ErrorMessages } from '../common/error-messages'; +import { buildVariantRequestHeaders } from '../common/utils'; interface EntryResponse { entry: T; @@ -10,6 +11,7 @@ export class Entry { private _entryUid: string; private _urlPath: string; protected _variants: string; + protected _variantsBranch: string; _queryParams: { [key: string]: string | number | string[] } = {}; constructor(client: AxiosInstance, contentTypeUid: string, entryUid: string) { this._client = client; @@ -17,6 +19,7 @@ export class Entry { this._entryUid = entryUid; this._urlPath = `/content_types/${this._contentTypeUid}/entries/${this._entryUid}`; this._variants = ''; + this._variantsBranch = ''; } /** @@ -39,20 +42,26 @@ export class Entry { /** * @method variants * @memberof Entry - * @description The variant header will be added to axios client + * @description The variant header will be added to axios client. Branch is optional. + * @param {string | string[]} variants - Variant UID or UIDs + * @param {string} [branchName] - Optional branch name sent as the `branch` header * @returns {Entry} * @example * import contentstack from '@contentstack/delivery-sdk' * * const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" }); * const result = await stack.contentType('abc').entry('entry_uid').variants('xyz').fetch(); + * const resultWithBranch = await stack.contentType('abc').entry('entry_uid').variants('xyz', 'branch_name').fetch(); */ - variants(variants: string | string[]): this { + variants(variants: string | string[], branchName?: string): this { if (Array.isArray(variants) && variants.length > 0) { this._variants = variants.join(','); } else if (typeof variants == 'string' && variants.length > 0) { this._variants = variants; } + if (typeof branchName === 'string' && branchName.length > 0) { + this._variantsBranch = branchName; + } return this; } @@ -189,10 +198,11 @@ export class Entry { contentTypeUid: this._contentTypeUid, entryUid: this._entryUid }; - if (this._variants) { + const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch); + if (variantHeaders) { getRequestOptions.headers = { ...getRequestOptions.headers, - 'x-cs-variant-uid': this._variants + ...variantHeaders }; } diff --git a/src/query/base-query.ts b/src/query/base-query.ts index 0d76cc0..05418b3 100644 --- a/src/query/base-query.ts +++ b/src/query/base-query.ts @@ -1,7 +1,7 @@ import { AxiosInstance, getData } from '@contentstack/core'; import { Pagination } from '../common/pagination'; import { FindResponse, params } from '../common/types'; -import { encodeQueryParams } from '../common/utils'; +import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils'; import type { Query } from './query'; export class BaseQuery extends Pagination { @@ -10,6 +10,7 @@ export class BaseQuery extends Pagination { protected _client!: AxiosInstance; protected _urlPath!: string; protected _variants!: string; + protected _variantsBranch!: string; /** * Helper method to cast this instance to Query type @@ -231,10 +232,11 @@ export class BaseQuery extends Pagination { contentTypeUid: this.extractContentTypeUidFromUrl() }; - if (this._variants) { + const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch); + if (variantHeaders) { getRequestOptions.headers = { ...getRequestOptions.headers, - 'x-cs-variant-uid': this._variants + ...variantHeaders }; } const response = await getData(this._client, this._urlPath, getRequestOptions); diff --git a/src/query/query.ts b/src/query/query.ts index fbfc92e..651dbad 100644 --- a/src/query/query.ts +++ b/src/query/query.ts @@ -1,13 +1,21 @@ import { AxiosInstance, getData } from '@contentstack/core'; import { BaseQuery } from './base-query'; import { BaseQueryParameters, QueryOperation, QueryOperator, TaxonomyQueryOperation, params, queryParams, FindResponse } from '../common/types'; -import { encodeQueryParams } from '../common/utils'; +import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils'; import { ErrorMessages } from '../common/error-messages'; export class Query extends BaseQuery { private _contentTypeUid?: string; - constructor(client: AxiosInstance, params: params, queryParams: queryParams, variants?: string, uid?: string, queryObj?: { [key: string]: any }) { + constructor( + client: AxiosInstance, + params: params, + queryParams: queryParams, + variants?: string, + uid?: string, + variantsBranchOrQueryObj?: string | { [key: string]: any }, + queryObj?: { [key: string]: any } + ) { super(); this._client = client; this._contentTypeUid = uid; @@ -16,11 +24,23 @@ export class Query extends BaseQuery { this._queryParams = queryParams || {}; this._variants = variants || ''; + let variantsBranch = ''; + let resolvedQueryObj: { [key: string]: any } | undefined; + + if (typeof variantsBranchOrQueryObj === 'string') { + variantsBranch = variantsBranchOrQueryObj; + resolvedQueryObj = queryObj; + } else if (variantsBranchOrQueryObj) { + resolvedQueryObj = variantsBranchOrQueryObj; + } + + this._variantsBranch = variantsBranch; + if (!uid) { this._urlPath = `/assets`; } - if (queryObj) { - this._parameters = { ...this._parameters, ...queryObj }; + if (resolvedQueryObj) { + this._parameters = { ...this._parameters, ...resolvedQueryObj }; } } // Validate if input is alphanumeric @@ -651,10 +671,11 @@ export class Query extends BaseQuery { contentTypeUid: this._contentTypeUid }; - if (this._variants) { + const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch); + if (variantHeaders) { getRequestOptions.headers = { ...getRequestOptions.headers, - 'x-cs-variant-uid': this._variants + ...variantHeaders }; } const response = await getData(this._client, this._urlPath, getRequestOptions); diff --git a/test/api/entry-variants-branch.spec.ts b/test/api/entry-variants-branch.spec.ts new file mode 100644 index 0000000..062811f --- /dev/null +++ b/test/api/entry-variants-branch.spec.ts @@ -0,0 +1,259 @@ +import { describe, it, expect } from '@jest/globals'; +import * as contentstack from '../../src/stack'; +import { stackInstance } from '../utils/stack-instance'; +import { TEntry } from './types'; + +const stack = stackInstance(); + +const contentTypeUid = process.env.COMPLEX_CONTENT_TYPE_UID || 'cybersecurity'; +const entryUid = process.env.COMPLEX_ENTRY_UID || ''; +const variantUid = process.env.VARIANT_UID || ''; +const branchUid = process.env.BRANCH_UID || 'main'; + +const hasEntryUid = !!entryUid; +const hasVariantUid = !!variantUid; + +const skipIfNoEntry = !hasEntryUid ? describe.skip : describe; +const skipIfNoVariant = !hasVariantUid ? describe.skip : describe; +const skipIfNoVariantOrEntry = !hasEntryUid || !hasVariantUid ? describe.skip : describe; + +describe('Entry Variants with Branch API Tests', () => { + skipIfNoVariantOrEntry('Single entry fetch with variant and branch', () => { + it('should fetch entry with single variant UID and branch', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry(entryUid) + .variants(variantUid, branchUid) + .fetch(); + + expect(result).toBeDefined(); + expect(result.uid).toBe(entryUid); + }); + + it('should fetch entry with multiple variant UIDs and branch', async () => { + const variantUids = [variantUid, 'variant_2', 'variant_3']; + + const result = await stack + .contentType(contentTypeUid) + .entry(entryUid) + .variants(variantUids, branchUid) + .fetch(); + + expect(result).toBeDefined(); + expect(result.uid).toBe(entryUid); + }); + + it('should fetch entry with variant only when branch is omitted (backward compatible)', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry(entryUid) + .variants(variantUid) + .fetch(); + + expect(result).toBeDefined(); + expect(result.uid).toBe(entryUid); + }); + + it('should fetch entry with variant, branch, and includeBranch metadata', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry(entryUid) + .variants(variantUid, branchUid) + .includeBranch() + .fetch(); + + expect(result).toBeDefined(); + expect(result.uid).toBe(entryUid); + if (result._branch) { + expect(typeof result._branch).toBe('string'); + } + }); + + it('should fetch entry with variant, branch, and includeMetadata', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry(entryUid) + .variants(variantUid, branchUid) + .includeMetadata() + .fetch(); + + expect(result).toBeDefined(); + expect(result.uid).toBe(entryUid); + }); + }); + + skipIfNoVariant('Entries find with variant and branch', () => { + it('should find entries with single variant UID and branch', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry() + .variants(variantUid, branchUid) + .limit(5) + .find(); + + expect(result).toBeDefined(); + expect(result.entries).toBeDefined(); + expect(Array.isArray(result.entries)).toBe(true); + + if (result.entries!.length > 0) { + expect(result.entries![0].uid).toBeDefined(); + } + }); + + it('should find entries with multiple variant UIDs and branch', async () => { + const variantUids = [variantUid, 'variant_2']; + + const result = await stack + .contentType(contentTypeUid) + .entry() + .variants(variantUids, branchUid) + .limit(5) + .find(); + + expect(result).toBeDefined(); + expect(result.entries).toBeDefined(); + expect(Array.isArray(result.entries)).toBe(true); + }); + + it('should find entries with variant only when branch is omitted (backward compatible)', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry() + .variants(variantUid) + .limit(5) + .find(); + + expect(result).toBeDefined(); + expect(result.entries).toBeDefined(); + }); + + it('should find entries with variant, branch, and includeCount', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry() + .variants(variantUid, branchUid) + .includeCount() + .limit(5) + .find(); + + expect(result).toBeDefined(); + expect(result.entries).toBeDefined(); + expect(typeof result.count).toBe('number'); + }); + }); + + skipIfNoVariantOrEntry('Query chain with variant and branch', () => { + it('should query entries with variant and branch via query()', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry() + .variants(variantUid, branchUid) + .query() + .equalTo('uid', entryUid) + .find(); + + expect(result).toBeDefined(); + expect(result.entries).toBeDefined(); + + if (result.entries && result.entries.length > 0) { + result.entries.forEach((entry) => { + expect(entry.uid).toBe(entryUid); + }); + } + }); + + it('should query entries with variant, branch, and pagination', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry() + .variants(variantUid, branchUid) + .limit(3) + .skip(0) + .find(); + + expect(result).toBeDefined(); + expect(result.entries).toBeDefined(); + expect(result.entries!.length).toBeLessThanOrEqual(3); + }); + }); + + skipIfNoEntry('Branch optional behavior', () => { + it('should fetch entry without variant or branch headers when neither is set', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry(entryUid) + .fetch(); + + expect(result).toBeDefined(); + expect(result.uid).toBe(entryUid); + }); + }); + + skipIfNoVariantOrEntry('Stack-level branch vs variants-level branch', () => { + it('should fetch entry when stack has default branch and variants() also passes branch', async () => { + const stackWithBranch = contentstack.stack({ + host: process.env.HOST || '', + apiKey: process.env.API_KEY || '', + deliveryToken: process.env.DELIVERY_TOKEN || '', + environment: process.env.ENVIRONMENT || '', + branch: branchUid, + live_preview: { + enable: false, + preview_token: process.env.PREVIEW_TOKEN || '', + host: process.env.LIVE_PREVIEW_HOST || '', + }, + }); + + expect(stackWithBranch.config.branch).toBe(branchUid); + + const result = await stackWithBranch + .contentType(contentTypeUid) + .entry(entryUid) + .variants(variantUid, branchUid) + .fetch(); + + expect(result).toBeDefined(); + expect(result.uid).toBe(entryUid); + }); + + it('should fetch entry with variant+branch on stack without stack-level branch config', async () => { + const result = await stack + .contentType(contentTypeUid) + .entry(entryUid) + .variants(variantUid, branchUid) + .fetch(); + + expect(result).toBeDefined(); + expect(result.uid).toBe(entryUid); + }); + }); + + skipIfNoVariantOrEntry('Error handling', () => { + it('should handle invalid variant UID with branch gracefully', async () => { + try { + await stack + .contentType(contentTypeUid) + .entry(entryUid) + .variants('invalid_variant_uid', branchUid) + .fetch(); + } catch (error) { + expect(error).toBeDefined(); + } + }); + + it('should return consistent results for repeated variant+branch requests', async () => { + const fetchEntry = () => + stack + .contentType(contentTypeUid) + .entry(entryUid) + .variants(variantUid, branchUid) + .fetch(); + + const [result1, result2] = await Promise.all([fetchEntry(), fetchEntry()]); + + expect(result1.uid).toBe(entryUid); + expect(result2.uid).toBe(entryUid); + expect(result1.uid).toBe(result2.uid); + }); + }); +}); diff --git a/test/unit/base-query.spec.ts b/test/unit/base-query.spec.ts index 3cc3575..cf7fb1d 100644 --- a/test/unit/base-query.spec.ts +++ b/test/unit/base-query.spec.ts @@ -158,12 +158,17 @@ class TestableBaseQuery extends BaseQuery { this._urlPath = urlPath; } this._variants = ''; + this._variantsBranch = ''; } setVariants(variants: string) { this._variants = variants; } + setVariantsBranch(branch: string) { + this._variantsBranch = branch; + } + setParameters(params: any) { this._parameters = params; } @@ -304,6 +309,18 @@ describe('BaseQuery find method', () => { expect(result).toEqual(entryFindMock); }); + it('should call find with variant and branch headers when branch is set', async () => { + mockClient.onGet('/content_types/test_uid/entries').reply((config) => { + expect(config.headers?.['x-cs-variant-uid']).toBe('variant1,variant2'); + expect(config.headers?.branch).toBe('branch_name'); + return [200, entryFindMock]; + }); + + query.setVariants('variant1,variant2'); + query.setVariantsBranch('branch_name'); + await query.find(); + }); + it('should call find with variants header when variants are set', async () => { mockClient.onGet('/content_types/test_uid/entries').reply((config) => { expect(config.headers?.['x-cs-variant-uid']).toBe('variant1,variant2'); diff --git a/test/unit/entries.spec.ts b/test/unit/entries.spec.ts index 461a9d6..ddcf1e8 100644 --- a/test/unit/entries.spec.ts +++ b/test/unit/entries.spec.ts @@ -275,6 +275,13 @@ describe('Variants test', () => { testVariantObj.variants([]); expect(testVariantObj.getVariants()).toBe(''); }); + + it('should set branch when branch name is provided', () => { + const testVariantObj = new TestVariants(client); + testVariantObj.variants(['variant1', 'variant2'], 'branch_name'); + expect(testVariantObj.getVariants()).toBe('variant1,variant2'); + expect(testVariantObj['_variantsBranch']).toBe('branch_name'); + }); }); describe('Find with encode and variants', () => { @@ -311,6 +318,28 @@ describe('Find with encode and variants', () => { await entry.find(); }); + it('should call find with variant and branch headers when branch is set', async () => { + mockClient.onGet('/content_types/contentTypeUid/entries').reply((config) => { + expect(config.headers?.['x-cs-variant-uid']).toBe('variant1,variant2'); + expect(config.headers?.branch).toBe('branch_name'); + return [200, entryFindMock]; + }); + + entry.variants(['variant1', 'variant2'], 'branch_name'); + await entry.find(); + }); + + it('should pass branch to query find when variants include branch', async () => { + mockClient.onGet('/content_types/contentTypeUid/entries').reply((config) => { + expect(config.headers?.['x-cs-variant-uid']).toBe('variant1'); + expect(config.headers?.branch).toBe('branch_name'); + return [200, entryFindMock]; + }); + + entry.variants('variant1', 'branch_name'); + await entry.query().find(); + }); + it('should handle find with both encode and variants', async () => { mockClient.onGet('/content_types/contentTypeUid/entries').reply((config) => { expect(config.headers?.['x-cs-variant-uid']).toBe('test-variant'); diff --git a/test/unit/entry.spec.ts b/test/unit/entry.spec.ts index 65730ae..0abd459 100644 --- a/test/unit/entry.spec.ts +++ b/test/unit/entry.spec.ts @@ -200,6 +200,13 @@ describe('Variants test', () => { testVariantObj.variants([]); expect(testVariantObj.getVariants()).toBe(''); }); + + it('should set branch when branch name is provided', () => { + const testVariantObj = new TestVariants(client); + testVariantObj.variants('variant1', 'branch_name'); + expect(testVariantObj.getVariants()).toBe('variant1'); + expect(testVariantObj['_variantsBranch']).toBe('branch_name'); + }); }); describe('Fetch with variants', () => { @@ -229,6 +236,32 @@ describe('Fetch with variants', () => { expect(result).toEqual(entryFetchMock.entry); }); + it('should call fetch with variant and branch headers when branch is set', async () => { + mockClient.onGet('/content_types/contentTypeUid/entries/entryUid').reply((config) => { + expect(config.headers?.['x-cs-variant-uid']).toBe('variant1'); + expect(config.headers?.branch).toBe('branch_name'); + return [200, entryFetchMock]; + }); + + entry.variants('variant1', 'branch_name'); + const result = await entry.fetch(); + + expect(result).toEqual(entryFetchMock.entry); + }); + + it('should call fetch with variant and branch headers for multiple variants', async () => { + mockClient.onGet('/content_types/contentTypeUid/entries/entryUid').reply((config) => { + expect(config.headers?.['x-cs-variant-uid']).toBe('variant1,variant2'); + expect(config.headers?.branch).toBe('branch_name'); + return [200, entryFetchMock]; + }); + + entry.variants(['variant1', 'variant2'], 'branch_name'); + const result = await entry.fetch(); + + expect(result).toEqual(entryFetchMock.entry); + }); + it('should call fetch without variant header when variants are not set', async () => { mockClient.onGet('/content_types/contentTypeUid/entries/entryUid').reply((config) => { expect(config.headers?.['x-cs-variant-uid']).toBeUndefined(); diff --git a/test/unit/utils.spec.ts b/test/unit/utils.spec.ts index 1786db3..dab66e7 100644 --- a/test/unit/utils.spec.ts +++ b/test/unit/utils.spec.ts @@ -1,4 +1,4 @@ -import { getHostforRegion, encodeQueryParams } from "../../src/common/utils"; +import { buildVariantRequestHeaders, getHostforRegion, encodeQueryParams } from "../../src/common/utils"; import { Region } from "../../src/common/types"; import { DUMMY_URL, @@ -215,6 +215,26 @@ describe("Utils functions", () => { }); }); + describe("buildVariantRequestHeaders function", () => { + it("should return variant and branch headers when both are provided", () => { + expect(buildVariantRequestHeaders("variant1,variant2", "branch_name")).toEqual({ + "x-cs-variant-uid": "variant1,variant2", + branch: "branch_name", + }); + }); + + it("should return only variant header when branch is not provided", () => { + expect(buildVariantRequestHeaders("variant1")).toEqual({ + "x-cs-variant-uid": "variant1", + }); + }); + + it("should return undefined when neither variant nor branch is provided", () => { + expect(buildVariantRequestHeaders("", "")).toBeUndefined(); + expect(buildVariantRequestHeaders("", undefined)).toBeUndefined(); + }); + }); + describe("encodeQueryParams function", () => { it("should encode special characters in strings", () => { const testParams = {