mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-11-11 17:16:43 -05:00
Fix: delay iframe DOM removal, handle onafterprint error for print in FF (#11237)
This commit is contained in:
commit
8b9ca75a90
@ -1489,6 +1489,8 @@ describe('DocumentDetailComponent', () => {
|
|||||||
mockContentWindow.onafterprint(new Event('afterprint'))
|
mockContentWindow.onafterprint(new Event('afterprint'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tick(500)
|
||||||
|
|
||||||
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
|
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
|
||||||
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
|
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
|
||||||
|
|
||||||
@ -1512,7 +1514,31 @@ describe('DocumentDetailComponent', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should show error toast if printing throws inside iframe', fakeAsync(() => {
|
const iframePrintErrorCases: Array<{
|
||||||
|
description: string
|
||||||
|
thrownError: Error
|
||||||
|
expectToast: boolean
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
description: 'should show error toast if printing throws inside iframe',
|
||||||
|
thrownError: new Error('focus failed'),
|
||||||
|
expectToast: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
'should suppress toast if cross-origin afterprint error occurs',
|
||||||
|
thrownError: new DOMException(
|
||||||
|
'Accessing onafterprint triggered a cross-origin violation',
|
||||||
|
'SecurityError'
|
||||||
|
),
|
||||||
|
expectToast: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
iframePrintErrorCases.forEach(({ description, thrownError, expectToast }) => {
|
||||||
|
it(
|
||||||
|
description,
|
||||||
|
fakeAsync(() => {
|
||||||
initNormally()
|
initNormally()
|
||||||
|
|
||||||
const appendChildSpy = jest
|
const appendChildSpy = jest
|
||||||
@ -1532,7 +1558,7 @@ describe('DocumentDetailComponent', () => {
|
|||||||
|
|
||||||
const mockContentWindow = {
|
const mockContentWindow = {
|
||||||
focus: jest.fn().mockImplementation(() => {
|
focus: jest.fn().mockImplementation(() => {
|
||||||
throw new Error('focus failed')
|
throw thrownError
|
||||||
}),
|
}),
|
||||||
print: jest.fn(),
|
print: jest.fn(),
|
||||||
onafterprint: null,
|
onafterprint: null,
|
||||||
@ -1563,7 +1589,13 @@ describe('DocumentDetailComponent', () => {
|
|||||||
mockIframe.onload(new Event('load'))
|
mockIframe.onload(new Event('load'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tick(200)
|
||||||
|
|
||||||
|
if (expectToast) {
|
||||||
expect(toastSpy).toHaveBeenCalled()
|
expect(toastSpy).toHaveBeenCalled()
|
||||||
|
} else {
|
||||||
|
expect(toastSpy).not.toHaveBeenCalled()
|
||||||
|
}
|
||||||
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
|
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
|
||||||
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
|
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
|
||||||
|
|
||||||
@ -1572,5 +1604,7 @@ describe('DocumentDetailComponent', () => {
|
|||||||
removeChildSpy.mockRestore()
|
removeChildSpy.mockRestore()
|
||||||
createObjectURLSpy.mockRestore()
|
createObjectURLSpy.mockRestore()
|
||||||
revokeObjectURLSpy.mockRestore()
|
revokeObjectURLSpy.mockRestore()
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
|
|||||||
import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer'
|
import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer'
|
||||||
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
|
||||||
import { DeviceDetectorService } from 'ngx-device-detector'
|
import { DeviceDetectorService } from 'ngx-device-detector'
|
||||||
import { BehaviorSubject, Observable, of, Subject } from 'rxjs'
|
import { BehaviorSubject, Observable, of, Subject, timer } from 'rxjs'
|
||||||
import {
|
import {
|
||||||
catchError,
|
catchError,
|
||||||
debounceTime,
|
debounceTime,
|
||||||
@ -1452,9 +1452,18 @@ export class DocumentDetailComponent
|
|||||||
URL.revokeObjectURL(blobUrl)
|
URL.revokeObjectURL(blobUrl)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// FF throws cross-origin error on onafterprint
|
||||||
|
const isCrossOriginAfterPrintError =
|
||||||
|
err instanceof DOMException &&
|
||||||
|
err.message.includes('onafterprint')
|
||||||
|
if (!isCrossOriginAfterPrintError) {
|
||||||
this.toastService.showError($localize`Print failed.`, err)
|
this.toastService.showError($localize`Print failed.`, err)
|
||||||
|
}
|
||||||
|
timer(100).subscribe(() => {
|
||||||
|
// delay to avoid FF print failure
|
||||||
document.body.removeChild(iframe)
|
document.body.removeChild(iframe)
|
||||||
URL.revokeObjectURL(blobUrl)
|
URL.revokeObjectURL(blobUrl)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user