Skip to content
20 changes: 20 additions & 0 deletions sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,23 @@ sonar.test.inclusions=src/**/*.test.ts,src/**/*.test.tsx,src/**/__tests__/**/*.{

# Coverage report
sonar.javascript.lcov.reportPaths=coverage/lcov.info

# Ignore "Refactor this code to not nest functions more than 4 levels deep." in test files,
# because it's mostly raised when describe-functions are used.
#
# Rule: typescript:S2004
# Name: "Refactor this code to not nest functions more than 4 levels deep."
# URL: https://rules.sonarsource.com/typescript/RSPEC-2004/
sonar.issue.ignore.multicriteria.e1.ruleKey=typescript:S2004
sonar.issue.ignore.multicriteria.e1.resourceKey=**/*.test.tsx

# Ignore "Using pseudorandom number generators (PRNGs) is security-sensitive" in stoyry book files.
# because it is safe in a non-security related Storybook demo file.
#
# Rule: typescript:S2245
# Name: "Using pseudorandom number generators (PRNGs) is security-sensitive"
# URL:
# - https://rules.sonarsource.com/typescript/RSPEC-2245/
# - https://sonarcloud.io/organizations/city-of-helsinki/rules?open=typescript%3AS2245&rule_key=typescript%3AS2245
sonar.issue.ignore.multicriteria.e2.ruleKey=typescript:S2245
sonar.issue.ignore.multicriteria.e2.resourceKey=**/*.stories.tsx
48 changes: 30 additions & 18 deletions src/common/utils/testImage.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,44 @@
/**
* Test that loading image is successful
* Tests if an image at a given URL loads successfully,
* using AbortController for clean event listener management.
*
* @param {string} [url] - The URL of the image to test.
* @return {Promise<void>} - A promise that resolves if the image loads, and rejects otherwise.
*/

/**
* @param {url} url - url of the image to test.
* @return {boolean} - Returns promise.
*/
const testImage = (url?: string): Promise<unknown> => {
const testImage = (url?: string): Promise<void> => {
if (!url) {
return Promise.reject(new Error('No image URL given'));
}

// Define the promise
const imgPromise = new Promise<void>((resolve, reject) => {
// Create the image
const imgElement = new Image();
const imgElement = new Image();
// Create the controller instance
const controller = new AbortController();
const { signal } = controller;

// When image is loaded, resolve the promise
imgElement.addEventListener('load', () => resolve());
return new Promise<void>((resolve, reject) => {
// The { signal } option ensures these listeners are grouped for cleanup
imgElement.addEventListener(
'load',
() => {
controller.abort(); // Triggers removal of ALL listeners attached to this signal
resolve();
},
{ signal },
);

// When there's an error during load, reject the promise
imgElement.addEventListener('error', () => reject());
imgElement.addEventListener(
'error',
() => {
controller.abort(); // Triggers removal of ALL listeners attached to this signal
// Reject with a standard Error object
reject(new Error(`Failed to load image from URL: ${url}`));
},
{ signal },
);

// Assign URL
imgElement.src = url;
});

return imgPromise;
// Note: The controller is self-contained and cleaned up when the promise resolves/rejects
};

export default testImage;
Loading