diff --git a/src/content/queries/cs-product.js b/src/content/queries/cs-product.js
index 78ae8d0..335880a 100644
--- a/src/content/queries/cs-product.js
+++ b/src/content/queries/cs-product.js
@@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/
-import { errorWithResponse, forceImagesHTTPS } from '../../utils/http.js';
+import { forceImagesHTTPS } from '../../utils/http.js';
import { gql, parseRating, parseSpecialToDate } from '../../utils/product.js';
/**
@@ -91,10 +91,6 @@ export const adapter = (config, productData) => {
} : null,
};
- if (!minPrice && !maxPrice) {
- throw errorWithResponse(400, 'no product price range found');
- }
-
if (config.attributeOverrides?.product) {
Object.entries(config.attributeOverrides.product).forEach(([key, value]) => {
product.attributeMap[key] = product.attributeMap[value] ?? product[key];
diff --git a/src/content/queries/cs-variants.js b/src/content/queries/cs-variants.js
index 778d0ae..a380dce 100644
--- a/src/content/queries/cs-variants.js
+++ b/src/content/queries/cs-variants.js
@@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/
-import { errorWithResponse, forceImagesHTTPS } from '../../utils/http.js';
+import { forceImagesHTTPS } from '../../utils/http.js';
import { gql, parseRating, parseSpecialToDate } from '../../utils/product.js';
/**
@@ -53,10 +53,6 @@ export const adapter = (config, variants) => variants.map(({ selections, product
selections: (selections ?? []).sort(),
};
- if (!minPrice && !maxPrice) {
- throw errorWithResponse(400, 'no variant price range found');
- }
-
if (config.attributeOverrides?.variant) {
Object.entries(config.attributeOverrides.variant).forEach(([key, value]) => {
variant.attributeMap[key] = variant.attributeMap[value] ?? variant[key];
diff --git a/src/templates/html/HTMLTemplate.js b/src/templates/html/HTMLTemplate.js
index d90537e..22e75d0 100644
--- a/src/templates/html/HTMLTemplate.js
+++ b/src/templates/html/HTMLTemplate.js
@@ -111,7 +111,7 @@ ${HTMLTemplate.metaName('twitter:title', product.name)}
${HTMLTemplate.metaName('twitter:image', image?.url)}
${HTMLTemplate.metaName('twitter:description', product.metaDescription)}
${HTMLTemplate.metaName('twitter:label1', 'Price')}
-${HTMLTemplate.metaName('twitter:data1', product.prices.final.amount)}
+${HTMLTemplate.metaName('twitter:data1', product.prices?.final?.amount)}
${HTMLTemplate.metaName('twitter:label2', 'Availability')}
${HTMLTemplate.metaName('twitter:data2', product.inStock ? 'In stock' : 'Out of stock')}`;
}
@@ -129,8 +129,8 @@ ${HTMLTemplate.metaName('externalId', product.externalId)}
${HTMLTemplate.metaName('addToCartAllowed', product.addToCartAllowed)}
${HTMLTemplate.metaName('inStock', product.inStock ? 'true' : 'false')}
${HTMLTemplate.metaProperty('product:availability', product.inStock ? 'In stock' : 'Out of stock')}
-${HTMLTemplate.metaProperty('product:price.amount', product.prices.final.amount)}
-${HTMLTemplate.metaProperty('product:price.currency', product.prices.final.currency)}`;
+${HTMLTemplate.metaProperty('product:price.amount', product.prices?.final?.amount)}
+${HTMLTemplate.metaProperty('product:price.currency', product.prices?.final?.currency)}`;
}
/**
@@ -271,8 +271,8 @@ ${HTMLTemplate.indent(this.renderProductItems(opt.items), 2)}`).join('\n')}
*/
renderVariantPrices(prices) {
return /* html */ `\
-
Regular: ${prices.regular.amount} ${prices.regular.currency}${HTMLTemplate.priceRange(prices.regular.minimumAmount, prices.regular.maximumAmount)}
-Final: ${prices.final.amount} ${prices.final.currency}${HTMLTemplate.priceRange(prices.final.minimumAmount, prices.final.maximumAmount)}
`;
+Regular: ${prices.regular?.amount} ${prices.regular?.currency}${HTMLTemplate.priceRange(prices.regular?.minimumAmount, prices.regular?.maximumAmount)}
+Final: ${prices.final?.amount} ${prices.final?.currency}${HTMLTemplate.priceRange(prices.final?.minimumAmount, prices.final?.maximumAmount)}
`;
}
/**
@@ -292,7 +292,7 @@ ${this.variants.map((v) => /* html */`\
${v.name}
${v.description}
${v.inStock ? 'inStock' : ''}
-${HTMLTemplate.indent(this.renderVariantPrices(v.prices), 4)}
+${v.prices ? HTMLTemplate.indent(this.renderVariantPrices(v.prices), 4) : ''}
${HTMLTemplate.indent(this.renderVariantImages(v.images), 6)}
diff --git a/src/templates/json/JSONTemplate.js b/src/templates/json/JSONTemplate.js
index 9247a5a..1285aee 100644
--- a/src/templates/json/JSONTemplate.js
+++ b/src/templates/json/JSONTemplate.js
@@ -140,14 +140,16 @@ export class JSONTemplate {
image: v.images?.[0]?.url ?? image,
availability: v.inStock ? 'InStock' : 'OutOfStock',
price: finalPrice,
- priceCurrency: variantPrices.final?.currency,
+ priceCurrency: variantPrices?.final?.currency,
gtin: v.attributeMap.gtin,
priceValidUntil: v.specialToDate,
aggregateRating: this.renderRating(v),
};
- if (finalPrice < regularPrice) {
- offer.priceSpecification = this.renderOffersPriceSpecification(v);
+ if (variantPrices) {
+ if (finalPrice < regularPrice) {
+ offer.priceSpecification = this.renderOffersPriceSpecification(v);
+ }
}
return pruneUndefined(offer);
diff --git a/test/templates/html/index.test.js b/test/templates/html/index.test.js
index 10e96a5..0fff73f 100644
--- a/test/templates/html/index.test.js
+++ b/test/templates/html/index.test.js
@@ -233,6 +233,36 @@ describe('Render Product HTML', () => {
});
});
+ it('template should allow for missing prices', () => {
+ config.confMap = {
+ '/us/p/{{urlkey}}/{{sku}}': {},
+ };
+
+ product.prices = undefined;
+ variations.forEach((variant) => {
+ variant.prices = undefined;
+ });
+
+ const html = htmlTemplateFromContext(DEFAULT_CONTEXT({ config }), product, variations).render();
+ dom = new JSDOM(html);
+ document = dom.window.document;
+
+ const metaPriceAmount = document.querySelector('meta[property="product:price.amount"]');
+ assert.strictEqual(metaPriceAmount.getAttribute('content'), 'undefined', 'meta[property="product:price:amount"] should be undefined');
+
+ const metaPriceCurrency = document.querySelector('meta[property="product:price.currency"]');
+ assert.strictEqual(metaPriceCurrency.getAttribute('content'), 'undefined', 'meta[property="product:price.currency"] should be undefined');
+
+ const jsonLdScript = document.querySelector('script[type="application/ld+json"]');
+ const jsonLd = JSON.parse(jsonLdScript.textContent);
+
+ jsonLd.offers.forEach((offer) => {
+ assert.strictEqual(offer.price, undefined, 'price should be undefined');
+ assert.strictEqual(offer.priceCurrency, undefined, 'priceCurrency should be undefined');
+ assert.strictEqual(offer.priceSpecification, undefined, 'priceSpecification should be undefined');
+ });
+ });
+
it('should display the correct product name in ', () => {
const h1 = document.querySelector('h1');
assert.strictEqual(h1.textContent, product.name, ' content does not match product name');