diff --git a/app/assets/javascripts/observability/utils/url_helpers.js b/app/assets/javascripts/observability/utils/url_helpers.js new file mode 100644 index 0000000000000000000000000000000000000000..27c55d062af9759457e995448857e32cad661339 --- /dev/null +++ b/app/assets/javascripts/observability/utils/url_helpers.js @@ -0,0 +1,23 @@ +export const buildIframeUrl = (path, baseUrl) => { + if (!baseUrl) { + return null; + } + try { + const urlWithPath = new URL(path, baseUrl); + return urlWithPath.toString(); + } catch (error) { + return baseUrl; + } +}; + +export const extractTargetPath = (path, baseUrl) => { + if (!path) { + return null; + } + try { + const url = new URL(path, baseUrl); + return url.pathname; + } catch (error) { + return path; + } +}; diff --git a/spec/frontend/observability/utils/url_helpers_spec.js b/spec/frontend/observability/utils/url_helpers_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..e2a86076165c33fad486ec574286e67cec81a7fa --- /dev/null +++ b/spec/frontend/observability/utils/url_helpers_spec.js @@ -0,0 +1,42 @@ +import { buildIframeUrl, extractTargetPath } from '~/observability/utils/url_helpers'; + +describe('URL Helper Utilities', () => { + describe('buildIframeUrl', () => { + it.each` + description | path | baseUrl | expected + ${'combines relative paths with base URL'} | ${'/dashboards'} | ${'https://example.com'} | ${'https://example.com/dashboards'} + ${'combines relative paths with base URL'} | ${'/dashboards/123/metrics'} | ${'https://example.com'} | ${'https://example.com/dashboards/123/metrics'} + ${'preserves query parameters'} | ${'/dashboards?tab=metrics'} | ${'https://example.com'} | ${'https://example.com/dashboards?tab=metrics'} + ${'preserves fragments'} | ${'/dashboards#overview'} | ${'https://example.com'} | ${'https://example.com/dashboards#overview'} + ${'handles absolute URLs by using directly'} | ${'https://other.com/absolute'} | ${'https://example.com'} | ${'https://other.com/absolute'} + ${'handles base URLs with existing paths'} | ${'/dashboards'} | ${'https://example.com/base'} | ${'https://example.com/dashboards'} + ${'handles base URLs with ports'} | ${'/dashboards'} | ${'https://example.com:8080'} | ${'https://example.com:8080/dashboards'} + ${'handles root paths'} | ${'/'} | ${'https://example.com'} | ${'https://example.com/'} + ${'returns base URL when construction fails'} | ${'invalid-path'} | ${'invalid-base-url'} | ${'invalid-base-url'} + `('$description: $path + $baseUrl = $expected', ({ path, baseUrl, expected }) => { + expect(buildIframeUrl(path, baseUrl)).toBe(expected); + }); + + it('returns null when baseUrl is empty', () => { + expect(buildIframeUrl('/dashboards', '')).toBeNull(); + }); + }); + + describe('extractTargetPath', () => { + it.each` + description | path | baseUrl | expected + ${'extracts pathname from combined URL'} | ${'/dashboards/123/metrics'} | ${'https://example.com'} | ${'/dashboards/123/metrics'} + ${'extracts pathname from root'} | ${'/'} | ${'https://example.com'} | ${'/'} + ${'ignores query parameters'} | ${'/dashboards?tab=metrics'} | ${'https://example.com'} | ${'/dashboards'} + ${'ignores fragments'} | ${'/dashboards#overview'} | ${'https://example.com'} | ${'/dashboards'} + ${'extracts pathname from absolute URLs'} | ${'https://other.com/absolute'} | ${'https://example.com'} | ${'/absolute'} + ${'returns original path when construction fails'} | ${'invalid-path'} | ${'invalid-base-url'} | ${'invalid-path'} + `('$description: $path + $baseUrl = $expected', ({ path, baseUrl, expected }) => { + expect(extractTargetPath(path, baseUrl)).toBe(expected); + }); + + it('returns null when path is empty', () => { + expect(extractTargetPath('', 'https://example.com')).toBeNull(); + }); + }); +});