-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8266 from cfpb/htmx-cache
Add TCCP htmx extensions to handle cache busting and filter tracking
- Loading branch information
Showing
7 changed files
with
183 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import htmx from 'htmx.org'; | ||
|
||
import webStorageProxy from '../../../js/modules/util/web-storage-proxy'; | ||
|
||
/** | ||
* htmx extension that adds an `htmx=true` query parameter | ||
* to URLs immediately before htmx makes a request and then | ||
* removes it before pushing it to the browser's history. | ||
* This allows endpoints fetched by htmx via AJAX to have | ||
* a URL that is different from their host page's URL, | ||
* reducing CDN caching mistaken identity bugs. | ||
* | ||
* There are other ways to handle htmx cache busting, | ||
* including a `getCacheBusterParam` config option and | ||
* using a `Vary: HX-Request` HTTP header, but we've | ||
* found them to be unreliable with our infrastructure. | ||
* | ||
* See https://htmx.org/docs/#caching | ||
* See https://htmx.org/extensions/ | ||
* See https://htmx.org/events/#htmx:configRequest | ||
* See https://htmx.org/events/#htmx:beforeHistoryUpdate | ||
*/ | ||
htmx.defineExtension('htmx-url-param', { | ||
onEvent: function (name, event) { | ||
if (name === 'htmx:configRequest') { | ||
event.detail.parameters.htmx = 'true'; | ||
} | ||
if (name === 'htmx:beforeHistoryUpdate') { | ||
event.detail.history.path = event.detail.history.path.replaceAll( | ||
/(&htmx=|(?<=\?)htmx=)true/g, | ||
'', | ||
); | ||
} | ||
}, | ||
}); | ||
|
||
/** | ||
* htmx extension that stores the page's path in web | ||
* storage whenever it's updated | ||
* See https://htmx.org/extensions/ | ||
* See https://htmx.org/events/#htmx:replacedInHistory | ||
*/ | ||
htmx.defineExtension('store-tccp-filter-path', { | ||
onEvent: function (name, event) { | ||
if (name === 'htmx:replacedInHistory') { | ||
webStorageProxy.setItem('tccp-filter-path', event.detail.path); | ||
} | ||
}, | ||
}); | ||
|
||
// Store the path on page load before htmx has started up | ||
webStorageProxy.setItem( | ||
'tccp-filter-path', | ||
window.location.pathname + window.location.search, | ||
); | ||
|
||
// Disable htmx localStorage cache. We've found CFPB pages are | ||
// large enough that htmx hits the localStorage limit pretty | ||
// quickly and throws harmless-but-annoying `historyCacheError` | ||
// console errors. | ||
// See https://htmx.org/attributes/hx-history/ | ||
// See https://htmx.org/events/#htmx:historyCacheError | ||
document.body.setAttribute('hx-history', 'false'); | ||
|
||
// Add htmx extensions to the dom and initialize them | ||
document.body.setAttribute('hx-ext', 'htmx-url-param, store-tccp-filter-path'); | ||
htmx.process(document.body); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
test/cypress/integration/consumer-tools/credit-cards/explore-cards-helpers.cy.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
export class ExploreCreditCards { | ||
openLandingPage() { | ||
cy.visit('/consumer-tools/credit-cards/explore-cards/'); | ||
} | ||
|
||
openResultsPage(filterParams) { | ||
const url = | ||
'/consumer-tools/credit-cards/explore-cards/cards?' + | ||
new URLSearchParams(filterParams).toString(); | ||
cy.visit(url); | ||
} | ||
|
||
selectCreditTier(tier) { | ||
cy.get('select[name=credit_tier]').select(tier); | ||
} | ||
|
||
selectLocation(location) { | ||
cy.get('select[name=location]').select(location); | ||
} | ||
|
||
selectSituation(situation) { | ||
cy.get('input[name=situations]').check(situation, { force: true }); | ||
} | ||
|
||
clickSubmitButton() { | ||
cy.get('button').contains('See cards for your situation').click(); | ||
} | ||
|
||
openFilterExpandable() { | ||
cy.get('.o-filterable-list-controls button.o-expandable_header').click(); | ||
} | ||
|
||
clickShowMoreButton() { | ||
cy.get('button') | ||
.contains('Show more results with higher interest rates') | ||
.click(); | ||
} | ||
|
||
getNumberResults() { | ||
return new Promise((resolve) => { | ||
return cy | ||
.get('.htmx-container') | ||
.not('.htmx-request') | ||
.get('.o-filterable-list-results .m-notification') | ||
.then((el) => resolve(Number(el.text().replace(/[^0-9]/g, '')))); | ||
}); | ||
} | ||
|
||
getNumberVisibleResults() { | ||
return new Promise((resolve) => { | ||
return cy | ||
.get('.htmx-container') | ||
.not('.htmx-request') | ||
.get('.o-filterable-list-results table tr') | ||
.filter(':visible') | ||
.then((el) => resolve(el.length)); | ||
}); | ||
} | ||
|
||
selectCheckboxFilter(name, value) { | ||
cy.get(`input[name=${name}]`).check(value, { force: true }); | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
test/cypress/integration/consumer-tools/credit-cards/explore-cards.cy.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { ExploreCreditCards } from './explore-cards-helpers.cy.js'; | ||
|
||
const exploreCards = new ExploreCreditCards(); | ||
|
||
describe('Explore credit cards landing page', () => { | ||
it('should show results tailored to the selected situation', () => { | ||
exploreCards.openLandingPage(); | ||
|
||
exploreCards.selectLocation('FL'); | ||
exploreCards.selectSituation('Earn rewards'); | ||
exploreCards.clickSubmitButton(); | ||
|
||
exploreCards.openFilterExpandable(); | ||
|
||
cy.get('#id_rewards input').should('be.checked'); | ||
}); | ||
}); | ||
|
||
describe('Explore credit cards results page', () => { | ||
it('should update results when user changes filters', () => { | ||
exploreCards.openResultsPage(); | ||
|
||
exploreCards.getNumberResults().then((oldNumResults) => { | ||
exploreCards.selectCheckboxFilter('rewards', 'Cashback rewards'); | ||
exploreCards.getNumberResults().then((newNumResults) => { | ||
expect(newNumResults).to.be.lt(oldNumResults); | ||
}); | ||
}); | ||
}); | ||
it('should show additional results when "Show more" button is clicked', () => { | ||
exploreCards.openResultsPage(); | ||
|
||
exploreCards.getNumberVisibleResults().then((oldNumResults) => { | ||
exploreCards.clickShowMoreButton(); | ||
exploreCards.getNumberVisibleResults().then((newNumResults) => { | ||
expect(oldNumResults).to.be.lt(newNumResults); | ||
}); | ||
}); | ||
}); | ||
// Disabling this test until we add card test data | ||
xit('should link to card detail pages', () => { | ||
exploreCards.openResultsPage(); | ||
|
||
cy.get('td[data-label="Credit card"] a').first().click(); | ||
|
||
cy.get('h1').contains('Customize for your situation').should('not.exist'); | ||
cy.get('h2').contains('Application requirements').should('exist'); | ||
}); | ||
}); |