@truedat/dd 8.12.6 → 8.12.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/package.json +4 -4
  2. package/src/components/CatalogCustomViewCards.js +2 -2
  3. package/src/components/CatalogViewConfigs.js +1 -1
  4. package/src/components/EditDomainForm.js +1 -1
  5. package/src/components/GrantApprovalRuleDeleteButton.js +1 -1
  6. package/src/components/GrantApprovalRuleForm.js +2 -1
  7. package/src/components/GrantFilters.js +1 -1
  8. package/src/components/GrantFiltersLoader.js +1 -1
  9. package/src/components/GrantRemovalDirectButton.js +1 -1
  10. package/src/components/GrantRequest.js +1 -1
  11. package/src/components/GrantRequestCancel.js +1 -1
  12. package/src/components/GrantsPagination.js +1 -1
  13. package/src/components/LineageForm.js +1 -1
  14. package/src/components/LineageGraph.js +95 -3801
  15. package/src/components/PendingStructureNotesPagination.js +1 -1
  16. package/src/components/ReferenceDatasetActions.js +2 -1
  17. package/src/components/ReferenceDatasetFieldSelector.js +1 -1
  18. package/src/components/ReferenceDatasetForm.js +2 -1
  19. package/src/components/ReferenceDatasetRoutes.js +1 -1
  20. package/src/components/ReferenceDatasets.js +1 -1
  21. package/src/components/ReferenceDatasetsTable.js +1 -1
  22. package/src/components/StructureDeleteButton.js +1 -1
  23. package/src/components/StructureFields.js +1 -1
  24. package/src/components/StructureFieldsDropdown.js +1 -1
  25. package/src/components/StructureGrantCart.js +1 -1
  26. package/src/components/StructureGrantCartCheckout.js +1 -1
  27. package/src/components/StructureGrantCartUserSelector.js +1 -1
  28. package/src/components/StructureGrantDropdown.js +1 -1
  29. package/src/components/StructureGrantSummaryButton.js +1 -1
  30. package/src/components/StructureNav.js +1 -1
  31. package/src/components/StructureNoteActions.js +2 -1
  32. package/src/components/StructureNotesEdit.js +1 -1
  33. package/src/components/StructureStructureForm.js +1 -1
  34. package/src/components/StructureStructureLinks.js +2 -2
  35. package/src/components/StructureTabPane.js +1 -1
  36. package/src/components/StructureTags.js +1 -1
  37. package/src/components/StructureTypeForm.js +1 -1
  38. package/src/components/StructuresBulkUpdate.js +3 -5
  39. package/src/components/StructuresDomainsUploadOption.js +1 -1
  40. package/src/components/StructuresPagination.js +1 -1
  41. package/src/components/StructuresUploadOption.js +1 -1
  42. package/src/components/SystemCards.js +1 -1
  43. package/src/components/SystemForm.js +1 -1
  44. package/src/components/Tag.js +1 -1
  45. package/src/components/TagCards.js +2 -1
  46. package/src/components/TagForm.js +2 -1
  47. package/src/components/__tests__/GrantApprovalRuleConditions.spec.js +1 -1
  48. package/src/components/__tests__/GrantRemovalDirectButton.spec.js +2 -1
  49. package/src/components/__tests__/GrantRequestApprovalForm.spec.js +2 -1
  50. package/src/components/__tests__/GrantRequestBulkActions.spec.js +2 -1
  51. package/src/components/__tests__/GrantRequestBulkApprovalForm.spec.js +2 -4
  52. package/src/components/__tests__/GrantRequestBulkApprovalPopup.spec.js +1 -1
  53. package/src/components/__tests__/GrantRequestBulkRoleSelector.spec.js +2 -2
  54. package/src/components/__tests__/GrantRequestLoader.spec.js +1 -1
  55. package/src/components/__tests__/GrantRequestRow.spec.js +1 -1
  56. package/src/components/__tests__/GrantRequestSummary.spec.js +1 -1
  57. package/src/components/__tests__/GrantRequestsDownloadButton.spec.js +2 -1
  58. package/src/components/__tests__/GrantRequestsLabelResults.spec.js +1 -1
  59. package/src/components/__tests__/GrantRequestsLoader.spec.js +1 -1
  60. package/src/components/__tests__/ImplementationStructuresLoader.spec.js +1 -1
  61. package/src/components/__tests__/LineageDownloadButton.spec.js +2 -1
  62. package/src/components/__tests__/LineageGraph.spec.js +1 -1
  63. package/src/components/__tests__/LineageGraphLayout.spec.js +1 -1
  64. package/src/components/__tests__/LineageGroup.spec.js +2 -1
  65. package/src/components/__tests__/LineageHierarchy.spec.js +1 -1
  66. package/src/components/__tests__/LineageResource.spec.js +2 -1
  67. package/src/components/__tests__/LineageResourceNode.spec.js +1 -1
  68. package/src/components/__tests__/PendingStructureNotesLabelResults.spec.js +2 -1
  69. package/src/components/__tests__/PendingStructureNotesPagination.spec.js +1 -1
  70. package/src/components/__tests__/ProfileExecution.spec.js +2 -1
  71. package/src/components/__tests__/ProfileExecutionLoader.spec.js +2 -1
  72. package/src/components/__tests__/ProfileGroupLoader.spec.js +2 -1
  73. package/src/components/__tests__/ProfileGroupMessage.spec.js +2 -1
  74. package/src/components/__tests__/ReferenceDatasetTable.spec.js +1 -1
  75. package/src/components/__tests__/StructureDeleteButton.spec.js +1 -1
  76. package/src/components/__tests__/StructureEvents.spec.js +11 -1
  77. package/src/components/__tests__/StructureGrantRequestButton.spec.js +3 -3
  78. package/src/components/__tests__/StructureItemRoot.spec.js +2 -1
  79. package/src/components/__tests__/StructureLoader.spec.js +1 -1
  80. package/src/components/__tests__/StructureProfileButton.spec.js +3 -2
  81. package/src/components/__tests__/StructureRelationRow.spec.js +3 -3
  82. package/src/components/__tests__/StructureRelations.spec.js +2 -1
  83. package/src/components/__tests__/StructureRoles.spec.js +2 -1
  84. package/src/components/__tests__/StructureRow.spec.js +2 -1
  85. package/src/components/__tests__/StructureSelectorInputField.spec.js +2 -1
  86. package/src/components/__tests__/StructureTagForm.spec.js +2 -1
  87. package/src/components/__tests__/StructureTypeLoader.spec.js +2 -1
  88. package/src/components/__tests__/StructureTypesLoader.spec.js +2 -1
  89. package/src/components/__tests__/StructuresDownloadOption.spec.js +1 -1
  90. package/src/components/__tests__/StructuresEditableDownloadOption.spec.js +1 -1
  91. package/src/components/__tests__/StructuresLabelResults.spec.js +1 -1
  92. package/src/components/__tests__/StructuresLoader.spec.js +2 -1
  93. package/src/components/__tests__/StructuresUploadOption.spec.js +1 -1
  94. package/src/components/__tests__/SystemEditButton.spec.js +2 -1
  95. package/src/components/__tests__/SystemLoader.spec.js +2 -1
  96. package/src/components/__tests__/UserSearchFiltersLoader.spec.js +2 -1
  97. package/src/components/__tests__/__snapshots__/PendingStructureNoteRow.spec.js.snap +7 -1
  98. package/src/components/__tests__/__snapshots__/PendingStructureNotes.spec.js.snap +11 -3
  99. package/src/components/__tests__/__snapshots__/PendingStructureNotesTable.spec.js.snap +11 -3
  100. package/src/selectors/__tests__/getGrantRequestsSearchColumns.spec.js +1 -1
  101. package/src/selectors/structureNotesColumnsSelector.js +9 -2
  102. package/src/styles/LineageGraph.less +3 -0
  103. package/src/utils/__tests__/lineageTopology.spec.js +1 -1
  104. package/src/utils/lineageGraph.js +3827 -0
@@ -1,6 +1,16 @@
1
- import { render, waitForLoad } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
+ import { waitForLoad } from "@truedat/test/waitForLoad";
2
3
  import { StructureEvents } from "../StructureEvents";
3
4
 
5
+ beforeAll(() => {
6
+ jest.useFakeTimers();
7
+ jest.setSystemTime(new Date("2025-01-01"));
8
+ });
9
+
10
+ afterAll(() => {
11
+ jest.useRealTimers();
12
+ });
13
+
4
14
  describe("<StructureEvents />", () => {
5
15
  it("matches the latest snapshot", async () => {
6
16
  const events = [
@@ -1,6 +1,6 @@
1
1
  import { waitFor } from "@testing-library/react";
2
2
  import userEvent from "@testing-library/user-event";
3
- import { render } from "@truedat/test/render";
3
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
4
4
  import { StructureGrantRequestButton } from "../StructureGrantRequestButton";
5
5
 
6
6
  describe("<StructureGrantRequestButton />", () => {
@@ -23,7 +23,7 @@ describe("<StructureGrantRequestButton />", () => {
23
23
  authorized: true,
24
24
  };
25
25
  const { getByRole } = render(<StructureGrantRequestButton {...props} />);
26
- userEvent.click(await getByRole("button"));
26
+ await userEvent.click(getByRole("button"));
27
27
  await waitFor(() => {
28
28
  expect(addRequest.mock.calls.length).toBe(1);
29
29
  expect(removeRequest.mock.calls.length).toBe(0);
@@ -40,7 +40,7 @@ describe("<StructureGrantRequestButton />", () => {
40
40
  authorized: true,
41
41
  };
42
42
  const { getByRole } = render(<StructureGrantRequestButton {...props} />);
43
- userEvent.click(await getByRole("button"));
43
+ await userEvent.click(getByRole("button"));
44
44
  await waitFor(() => {
45
45
  expect(addRequest.mock.calls.length).toBe(0);
46
46
  expect(removeRequest.mock.calls.length).toBe(1);
@@ -1,6 +1,7 @@
1
1
  import { waitFor } from "@testing-library/react";
2
2
  import userEvent from "@testing-library/user-event";
3
- import { render, waitForLoad } from "@truedat/test/render";
3
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
4
+ import { waitForLoad } from "@truedat/test/waitForLoad";
4
5
  import { StructureItemRoot } from "../StructureItemRoot";
5
6
 
6
7
  const mockHistory = jest.fn();
@@ -1,4 +1,4 @@
1
- import { render } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
2
  import { useWebContext } from "@truedat/core/webContext";
3
3
  import { StructureLoader } from "../StructureLoader";
4
4
 
@@ -1,6 +1,7 @@
1
1
  import userEvent from "@testing-library/user-event";
2
2
  import { waitFor } from "@testing-library/react";
3
- import { render, waitForLoad } from "@truedat/test/render";
3
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
4
+ import { waitForLoad } from "@truedat/test/waitForLoad";
4
5
  import { StructureProfileButton } from "../StructureProfileButton";
5
6
 
6
7
  describe("<StructureProfileButton />", () => {
@@ -25,7 +26,7 @@ describe("<StructureProfileButton />", () => {
25
26
  const rendered = render(<StructureProfileButton {...props} />);
26
27
  await waitForLoad(rendered);
27
28
 
28
- userEvent.click(rendered.getByRole("button"));
29
+ await userEvent.click(rendered.getByRole("button"));
29
30
 
30
31
  await waitFor(() => {
31
32
  expect(createProfileGroup).toHaveBeenCalledWith({
@@ -103,7 +103,7 @@ describe("<StructureRelationRow />", () => {
103
103
  relation,
104
104
  columns: [{ name: "links", fieldDecorator: linksDecorator }],
105
105
  };
106
- const rendered = render(<StructureRelationRow {...props} />);
106
+ const rendered = renderRow(props);
107
107
  await waitForLoad(rendered);
108
108
 
109
109
  expect(rendered.getByText(/foo concept/i)).toBeInTheDocument();
@@ -122,7 +122,7 @@ describe("<StructureRelationRow />", () => {
122
122
  relation,
123
123
  columns: [{ name: "links", fieldDecorator: linksDecorator }],
124
124
  };
125
- const rendered = render(<StructureRelationRow {...props} />);
125
+ const rendered = renderRow(props);
126
126
  await waitForLoad(rendered);
127
127
 
128
128
  expect(rendered.getByText(/foo concept/i)).toBeInTheDocument();
@@ -140,7 +140,7 @@ describe("<StructureRelationRow />", () => {
140
140
  relation,
141
141
  columns: [{ name: "links", fieldDecorator: linksDecorator }],
142
142
  };
143
- const rendered = render(<StructureRelationRow {...props} />);
143
+ const rendered = renderRow(props);
144
144
  await waitForLoad(rendered);
145
145
 
146
146
  expect(rendered.queryByRole("link")).not.toBeInTheDocument();
@@ -1,4 +1,5 @@
1
- import { render, waitForLoad } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
+ import { waitForLoad } from "@truedat/test/waitForLoad";
2
3
  import _ from "lodash/fp";
3
4
  import { StructureRelations } from "../StructureRelations";
4
5
  import StructureRelationRow from "../StructureRelationRow";
@@ -1,4 +1,5 @@
1
- import { render, waitForLoad } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
+ import { waitForLoad } from "@truedat/test/waitForLoad";
2
3
  import StructureRoles from "../StructureRoles";
3
4
 
4
5
  jest.mock("@truedat/core/hooks/useAclEntries", () => ({
@@ -1,6 +1,7 @@
1
1
  import userEvent from "@testing-library/user-event";
2
2
  import { waitFor } from "@testing-library/react";
3
- import { render, waitForLoad } from "@truedat/test/render";
3
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
4
+ import { waitForLoad } from "@truedat/test/waitForLoad";
4
5
  import _ from "lodash/fp";
5
6
  import { StructureRow } from "../StructureRow";
6
7
 
@@ -1,6 +1,7 @@
1
1
  import userEvent from "@testing-library/user-event";
2
2
  import { waitFor } from "@testing-library/react";
3
- import { render, waitForLoad } from "@truedat/test/render";
3
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
4
+ import { waitForLoad } from "@truedat/test/waitForLoad";
4
5
  import { StructureSelectorInputField } from "../StructureSelectorInputField";
5
6
 
6
7
  // Mock the imported components
@@ -1,6 +1,7 @@
1
1
  import userEvent from "@testing-library/user-event";
2
2
  import { waitFor } from "@testing-library/react";
3
- import { render, waitForLoad } from "@truedat/test/render";
3
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
4
+ import { waitForLoad } from "@truedat/test/waitForLoad";
4
5
  import { StructureTagForm } from "../StructureTagForm";
5
6
 
6
7
  describe("<StructureTagForm />", () => {
@@ -1,4 +1,5 @@
1
- import { render, waitForLoad } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
+ import { waitForLoad } from "@truedat/test/waitForLoad";
2
3
  import { StructureTypeLoader } from "../StructureTypeLoader";
3
4
 
4
5
  jest.mock("react-router", () => ({
@@ -1,4 +1,5 @@
1
- import { render, waitForLoad } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
+ import { waitForLoad } from "@truedat/test/waitForLoad";
2
3
  import { StructureTypesLoader } from "../StructureTypesLoader";
3
4
 
4
5
  describe("<StructureTypesLoader />", () => {
@@ -1,4 +1,4 @@
1
- import { render } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
2
  import SearchContextWrapper from "@truedat/core/components/common/SearchContextWrapper";
3
3
  import { StructuresDownloadOption } from "../StructuresDownloadOption";
4
4
  import en from "../../messages/en";
@@ -1,4 +1,4 @@
1
- import { render } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
2
  import SearchContextWrapper from "@truedat/core/components/common/SearchContextWrapper";
3
3
  import { StructuresEditableDownloadOption } from "../StructuresEditableDownloadOption";
4
4
 
@@ -1,4 +1,4 @@
1
- import { render } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
2
  import { SearchContextProvider } from "@truedat/core/search/SearchContext";
3
3
  import StructuresLabelResults from "../StructuresLabelResults";
4
4
 
@@ -1,4 +1,5 @@
1
- import { render, waitForLoad } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
+ import { waitForLoad } from "@truedat/test/waitForLoad";
2
3
  import { StructuresLoader } from "../StructuresLoader";
3
4
 
4
5
  const getProps = () => ({
@@ -1,5 +1,5 @@
1
1
  // only admin user
2
- import { render } from "@truedat/test/render";
2
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
3
3
  import { StructuresUploadOption } from "../StructuresUploadOption";
4
4
 
5
5
  describe("<StructuresUploadOption />", () => {
@@ -1,4 +1,5 @@
1
- import { render, waitForLoad } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
+ import { waitForLoad } from "@truedat/test/waitForLoad";
2
3
  import { SystemEditButton } from "../SystemEditButton";
3
4
 
4
5
  jest.mock("@truedat/core/hooks", () => ({
@@ -1,4 +1,5 @@
1
- import { render, waitForLoad } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
+ import { waitForLoad } from "@truedat/test/waitForLoad";
2
3
  import { SystemLoader } from "../SystemLoader";
3
4
 
4
5
  jest.mock("react-router", () => ({
@@ -1,4 +1,5 @@
1
- import { render, waitForLoad } from "@truedat/test/render";
1
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
2
+ import { waitForLoad } from "@truedat/test/waitForLoad";
2
3
  import { UserSearchFiltersLoader } from "../UserSearchFiltersLoader";
3
4
 
4
5
  describe("<UserSearchFiltersLoader />", () => {
@@ -31,7 +31,13 @@ exports[`<PendingStructureNoteRow /> matches the latest snapshot 1`] = `
31
31
  <td
32
32
  class=""
33
33
  >
34
- bar domain, baz domain
34
+ <span>
35
+ bar domain
36
+ </span>
37
+ ,
38
+ <span>
39
+ baz domain
40
+ </span>
35
41
  </td>
36
42
  <td
37
43
  class=""
@@ -135,7 +135,7 @@ exports[`<PendingStructureNotes /> matches the latest snapshot 1`] = `
135
135
  pendingStructureNotes.props.system
136
136
  </th>
137
137
  <th
138
- class="one wide disabled"
138
+ class="two wide disabled"
139
139
  >
140
140
  pendingStructureNotes.props.domain
141
141
  </th>
@@ -176,7 +176,9 @@ exports[`<PendingStructureNotes /> matches the latest snapshot 1`] = `
176
176
  <td
177
177
  class=""
178
178
  >
179
- bar domain
179
+ <span>
180
+ bar domain
181
+ </span>
180
182
  </td>
181
183
  <td
182
184
  class=""
@@ -215,7 +217,13 @@ exports[`<PendingStructureNotes /> matches the latest snapshot 1`] = `
215
217
  <td
216
218
  class=""
217
219
  >
218
- foo domain 1, foo domain 2
220
+ <span>
221
+ foo domain 1
222
+ </span>
223
+ ,
224
+ <span>
225
+ foo domain 2
226
+ </span>
219
227
  </td>
220
228
  <td
221
229
  class=""
@@ -27,7 +27,7 @@ exports[`<PendingStructureNotesTable /> matches the latest snapshot 1`] = `
27
27
  System
28
28
  </th>
29
29
  <th
30
- class="one wide disabled"
30
+ class="two wide disabled"
31
31
  >
32
32
  Domain
33
33
  </th>
@@ -68,7 +68,9 @@ exports[`<PendingStructureNotesTable /> matches the latest snapshot 1`] = `
68
68
  <td
69
69
  class=""
70
70
  >
71
- bar domain
71
+ <span>
72
+ bar domain
73
+ </span>
72
74
  </td>
73
75
  <td
74
76
  class=""
@@ -107,7 +109,13 @@ exports[`<PendingStructureNotesTable /> matches the latest snapshot 1`] = `
107
109
  <td
108
110
  class=""
109
111
  >
110
- foo domain 1, foo domain 2
112
+ <span>
113
+ foo domain 1
114
+ </span>
115
+ ,
116
+ <span>
117
+ foo domain 2
118
+ </span>
111
119
  </td>
112
120
  <td
113
121
  class=""
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import { render } from "@truedat/test/render";
2
+ import { renderWithIntl as render } from "@truedat/test/renderWithIntl";
3
3
  import {
4
4
  defaultGrantRequestsSearchTableColumns,
5
5
  getGrantRequestsSearchColumns,
@@ -1,9 +1,11 @@
1
1
  import _ from "lodash/fp";
2
+ import { Fragment } from "react";
2
3
  import PropTypes from "prop-types";
3
4
  import { createSelector } from "reselect";
4
5
  import { Link } from "react-router";
5
6
  import { linkTo } from "@truedat/core/routes";
6
7
  import { FormattedMessage } from "react-intl";
8
+ import { DomainTooltip } from "@truedat/bg/taxonomy/components/DomainPath";
7
9
 
8
10
  const StructureLink = (structure) =>
9
11
  structure?.id ? (
@@ -16,7 +18,12 @@ const translateDecorator = (id) =>
16
18
  id ? <FormattedMessage id={id} defaultMessage={id} /> : null;
17
19
 
18
20
  const DomainDecorator = (domains) =>
19
- _.flow(_.map("name"), _.join(", "))(domains);
21
+ domains?.map((domain, index) => (
22
+ <Fragment key={domain.id ?? domain.name}>
23
+ {index > 0 && ", "}
24
+ <DomainTooltip domain={domain} />
25
+ </Fragment>
26
+ ));
20
27
 
21
28
  DomainDecorator.propTypes = {
22
29
  domains: PropTypes.array,
@@ -57,7 +64,7 @@ export const defaultStructureNoteColumns = [
57
64
  name: "domain",
58
65
  fieldSelector: _.pathOr([], "domains"),
59
66
  fieldDecorator: DomainDecorator,
60
- width: 1,
67
+ width: 2,
61
68
  },
62
69
  {
63
70
  name: "path",
@@ -1538,6 +1538,9 @@
1538
1538
  stroke-opacity: 0.4;
1539
1539
  filter: none;
1540
1540
  }
1541
+ .lineage-graph__stage .td-graph-edge--focus-dimmed.td-graph-edge--hovered,
1542
+ .lineage-graph__stage-surface
1543
+ .td-graph-edge--focus-dimmed.td-graph-edge--hovered,
1541
1544
  .lineage-graph__stage .td-graph-edge--focus-dimmed.td-graph-edge--node-hovered,
1542
1545
  .lineage-graph__stage-surface
1543
1546
  .td-graph-edge--focus-dimmed.td-graph-edge--node-hovered {
@@ -10,7 +10,7 @@ import {
10
10
  lineageEdge,
11
11
  collapseLineageNodes,
12
12
  aggregateCollapsedEdges,
13
- } from "../../components/LineageGraph";
13
+ } from "../lineageGraph";
14
14
 
15
15
  // Synthetic multi-stage lineage with nested containers and Glue field aliases.
16
16
  // Keep this fixture self-contained so CI never depends on a local data export.