Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 6 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ Changes:
- None yet

Fixes:
- None yet
- Stop attributing district docket report uploads to the per-tab cached
case id when the page context yields none: derive the id from the
sheet's own goDLS document links first. The cached id can belong to a
different case viewed earlier in the same tab, which uploaded one
case's entire docket sheet under another case's `pacer_case_id` and
merged it into the wrong archive docket.

For developers:
- Nothing yet
Expand Down
137 changes: 137 additions & 0 deletions spec/ContentDelegateSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,16 @@ describe('The ContentDelegate class', function () {
});

describe('when the docket page is not an interstitial page', function () {
const makeLink = (caseId, docId) => {
const a = document.createElement('a');
a.href = `https://ecf.canb.uscourts.gov/doc1/${docId}`;
a.setAttribute(
'onclick',
`goDLS('/doc1/${docId}','${caseId}','5','','','1','','','');` +
'return(false);'
);
return a;
};
beforeEach(function () {
clearDocumentBody();
table = document.createElement('table');
Expand Down Expand Up @@ -670,6 +680,133 @@ describe('The ContentDelegate class', function () {
expect(button.length).toBe(1);
});

it('prefers the goDLS case id over a stale tab id', async function () {
// The tab's cached caseId ('531591' in the storage mock)
// belongs to another case; the page's goDLS links must win.
const link = makeLink('177277', '034031424909');
const cd = new ContentDelegate(
tabId,
docketDisplayUrl,
docketDisplayPath,
'canb',
undefined, // no case id derived from url/referrer/inputs
undefined,
[link]
);
dispatchBackgroundNotifier = jasmine.createSpy();
dispatchBackgroundFetch = jasmine
.createSpy()
.and.callFake(fakeBackgroundFetch);
spyOn(history, 'replaceState');
await cd.handleDocketDisplayPage();
expect(dispatchBackgroundFetch).toHaveBeenCalledWith(
jasmine.objectContaining({
action: 'upload',
data: jasmine.objectContaining({ pacer_case_id: '177277' }),
})
);
expect(dispatchBackgroundFetch).not.toHaveBeenCalledWith(
jasmine.objectContaining({
data: jasmine.objectContaining({ pacer_case_id: '531591' }),
})
);
// the stale cached id is corrected for later pages in this tab
expect(window.chrome.storage.local.set).toHaveBeenCalledWith(
jasmine.objectContaining({
1234: jasmine.objectContaining({ caseId: '177277' }),
}),
jasmine.any(Function)
);
});

it('never overrides a context-derived case id', async function () {
// On a consolidated MEMBER docket the document links point at
// the LEAD case, so an id the page context provided (url,
// inputs, referrer) always beats the goDLS majority.
const link = makeLink('177277', '034031424909'); // lead case
const cd = new ContentDelegate(
tabId,
docketDisplayUrl,
docketDisplayPath,
'canb',
'531591', // the member case, from the page context
undefined,
[link]
);
dispatchBackgroundNotifier = jasmine.createSpy();
dispatchBackgroundFetch = jasmine
.createSpy()
.and.callFake(fakeBackgroundFetch);
spyOn(history, 'replaceState');
await cd.handleDocketDisplayPage();
expect(dispatchBackgroundFetch).toHaveBeenCalledWith(
jasmine.objectContaining({
action: 'upload',
data: jasmine.objectContaining({ pacer_case_id: '531591' }),
})
);
});

it('uses the majority goDLS id on merged sheets', async function () {
// Consolidated/MDL dockets legitimately link member cases'
// documents — a stray link must not outvote the sheet.
const cd = new ContentDelegate(
tabId,
docketDisplayUrl,
docketDisplayPath,
'canb',
undefined,
undefined,
[
makeLink('177277', '034031424909'),
makeLink('177277', '034031424910'),
makeLink('177277', '034031424911'),
makeLink('999999', '034031424912'), // stray member-case link
]
);
dispatchBackgroundNotifier = jasmine.createSpy();
dispatchBackgroundFetch = jasmine
.createSpy()
.and.callFake(fakeBackgroundFetch);
spyOn(history, 'replaceState');
await cd.handleDocketDisplayPage();
expect(dispatchBackgroundFetch).toHaveBeenCalledWith(
jasmine.objectContaining({
action: 'upload',
data: jasmine.objectContaining({ pacer_case_id: '177277' }),
})
);
});

it('falls back when the goDLS ids are tied', async function () {
// Equal votes for two ids: don't guess; use the cached id
// ('531591' in the storage mock) as before.
const cd = new ContentDelegate(
tabId,
docketDisplayUrl,
docketDisplayPath,
'canb',
undefined,
undefined,
[
makeLink('177277', '034031424909'),
makeLink('888888', '034031424910'),
]
);
dispatchBackgroundNotifier = jasmine.createSpy();
dispatchBackgroundFetch = jasmine
.createSpy()
.and.callFake(fakeBackgroundFetch);
spyOn(history, 'replaceState');
await cd.handleDocketDisplayPage();
expect(dispatchBackgroundFetch).toHaveBeenCalledWith(
jasmine.objectContaining({
action: 'upload',
data: jasmine.objectContaining({ pacer_case_id: '531591' }),
})
);
});

it('calls uploadDocket and responds to a negative result', async function () {
const cd = docketDisplayContentDelegate;
dispatchBackgroundNotifier = jasmine.createSpy();
Expand Down
30 changes: 24 additions & 6 deletions src/content_delegate.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,30 @@ ContentDelegate.prototype.handleDocketDisplayPage = async function () {
// check if appellate
// let isAppellate = PACER.isAppellateCourt(this.court);

// if the content_delegate didn't pull the case Id on initialization,
// check the page for a lead case dktrpt url.
const tabStorage = await getItemsFromStorage(this.tabId);
this.pacer_case_id = this.pacer_case_id
? this.pacer_case_id
: tabStorage.caseId;
// If initialization yielded no case id, derive it from the page itself:
// the sheet's goDLS document links carry it. The per-tab cached id is
// the LAST resort — it can belong to a different case viewed earlier in
// this tab. An id the page context already provided is never overridden:
// consolidated member dockets legitimately link the lead case's
// documents, so the goDLS majority only speaks when nothing else does.
if (!this.pacer_case_id) {
const pageCaseId = PACER.getCaseIdFromDocketDisplayLinks(this.links);
const tabStorage = await getItemsFromStorage(this.tabId);
Comment thread
playfulart marked this conversation as resolved.
Outdated
if (pageCaseId) {
if (tabStorage.caseId && tabStorage.caseId !== pageCaseId) {
console.warn(
`RECAP: Ignoring cached case id ${tabStorage.caseId}: the ` +
`docket's own links say it belongs to case ${pageCaseId}.`
);
}
this.pacer_case_id = pageCaseId;
// Correct the cached id so caseless pages downstream (e.g.
// attachment menus) inherit the right one.
Comment thread
playfulart marked this conversation as resolved.
Outdated
await saveCaseIdinTabStorage({ tabId: this.tabId }, pageCaseId);
} else {
this.pacer_case_id = tabStorage.caseId;
}
}

// If we don't have this.pacer_case_id at this point, punt.
if (!this.pacer_case_id) return;
Expand Down
29 changes: 29 additions & 0 deletions src/pacer.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,35 @@ let PACER = {
}
},

// Returns the case id a docket report page asserts about itself: the
// strict majority of the de_caseid values in its document links' goDLS()
// handlers. Majority, because consolidated/MDL sheets legitimately link
Comment thread
playfulart marked this conversation as resolved.
Outdated
// member cases' documents. Returns undefined when there is no goDLS
// evidence or the top ids are tied.
getCaseIdFromDocketDisplayLinks: function (links) {
Comment thread
playfulart marked this conversation as resolved.
let tally = {};
for (let i = 0; i < links.length; i++) {
if (!PACER.isDoc1Url(links[i].href)) {
continue;
}
let goDLS = PACER.parseGoDLSFunction(links[i].getAttribute('onclick'));
if (goDLS && goDLS.de_caseid && goDLS.de_caseid !== '0') {
tally[goDLS.de_caseid] = (tally[goDLS.de_caseid] || 0) + 1;
}
}
let best;
let tied = false;
for (let caseId of Object.keys(tally)) {
if (best === undefined || tally[caseId] > tally[best]) {
best = caseId;
tied = false;
} else if (tally[caseId] === tally[best]) {
tied = true;
}
}
return tied ? undefined : best;
},

// Given a URL that satisfies isDocketQueryUrl, gets its case number.
getCaseNumberFromUrls: function (urls) {
// Iterate over an array of URLs and get the case number from the
Expand Down
Loading