diff --git a/README.md b/README.md index adb90a2..c7dd5fb 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ The pino package is used within The current supported sites are listed below. - Amazon +- Crutchfield (requires Node 16) - Home Depot Don't see your site listed? Please consider [contributing](#contributing) to the project! diff --git a/src/sites/Crutchfield.ts b/src/sites/Crutchfield.ts new file mode 100644 index 0000000..b206284 --- /dev/null +++ b/src/sites/Crutchfield.ts @@ -0,0 +1,41 @@ +import Site from '../Site'; +import logger from '../logger'; +import * as siteUtils from '../site-utils'; +import { CheerioAPI } from 'cheerio'; + +export default class Crutchfield implements Site { + constructor(protected uri: string) { + if (!Crutchfield.isSite(uri)) { + throw new Error(`invalid uri for Crutchfield: ${uri}`); + } + } + + getURIForPageData(): string { + return this.uri; + } + + findPriceOnPage($: CheerioAPI): number { + const selectors = ['.pricing-wrapper .price.js-price']; + + const priceString = siteUtils.findContentOnPage($, selectors); + + if (!priceString) { + logger.error('price not found on Crutchfield page, uri: %s', this.uri); + return -1; + } + + const price = siteUtils.processPrice(priceString); + + return price; + } + + static isSite(uri: string): boolean { + if (uri.indexOf('www.crutchfield.com') > -1) { + return true; + } else { + return false; + } + } +} + +module.exports = Crutchfield; diff --git a/test/e2e/Crutchfield.e2e.test.ts b/test/e2e/Crutchfield.e2e.test.ts new file mode 100644 index 0000000..4c65912 --- /dev/null +++ b/test/e2e/Crutchfield.e2e.test.ts @@ -0,0 +1,12 @@ +import { priceFinder, verifyPrice } from './testHelper'; + +describe('price-finder for Crutchfield URIs', () => { + // Headphones + const uri = + 'https://www.crutchfield.com/p_158WF1KX5B/Sony-WF-1000XM5-Black.html?tp=60828'; + + it('should respond with a price', async () => { + const price = await priceFinder.findItemPrice(uri); + verifyPrice(price); + }); +}); diff --git a/test/unit/sites/Crutchfield.test.ts b/test/unit/sites/Crutchfield.test.ts new file mode 100644 index 0000000..f01adf2 --- /dev/null +++ b/test/unit/sites/Crutchfield.test.ts @@ -0,0 +1,60 @@ +import * as cheerio from 'cheerio'; +import Crutchfield from '../../../src/sites/Crutchfield'; + +const VALID_URI = 'http://www.crutchfield.com/product'; +const INVALID_URI = 'http://www.bad.com/product'; + +describe('The Crutchfield Site', () => { + describe('isSite() function', () => { + it('should return true for a correct site', () => { + expect(Crutchfield.isSite(VALID_URI)).toEqual(true); + }); + + it('should return false for a bad site', () => { + expect(Crutchfield.isSite(INVALID_URI)).toEqual(false); + }); + }); + + it('should throw an exception trying to create a new Crutchfield with an incorrect uri', () => { + expect(() => { + new Crutchfield(INVALID_URI); + }).toThrow(/invalid uri for Crutchfield/); + }); + + describe('a new Crutchfield Site', () => { + let site: Crutchfield; + + beforeEach(() => { + site = new Crutchfield(VALID_URI); + }); + + it('should return the same URI for getURIForPageData()', () => { + expect(site.getURIForPageData()).toEqual(VALID_URI); + }); + + describe('with a populated page', () => { + let $: cheerio.CheerioAPI; + let bad$: cheerio.CheerioAPI; + let price: number; + + beforeEach(() => { + price = 499.0; + + $ = cheerio.load( + `
$${price}
`, + ); + bad$ = cheerio.load('

Nothin here

'); + }); + + it('should return the price when displayed on the page', () => { + const priceFound = site.findPriceOnPage($); + expect(priceFound).toEqual(price); + }); + + it('should return -1 when the price is not found', () => { + const priceFound = site.findPriceOnPage(bad$); + expect(priceFound).toEqual(-1); + }); + }); + }); +});