@rancher/shell 0.4.0 → 0.5.1

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 (243) hide show
  1. package/assets/images/providers/ovhcloudmks.svg +122 -0
  2. package/assets/images/providers/ovhcloudpubliccloud.svg +122 -0
  3. package/assets/styles/global/_layout.scss +99 -0
  4. package/assets/translations/en-us.yaml +30 -5
  5. package/assets/translations/zh-hans.yaml +1 -1
  6. package/babel.config.js +7 -1
  7. package/chart/monitoring/alerting/index.vue +7 -21
  8. package/chart/monitoring/grafana/index.vue +55 -0
  9. package/chart/monitoring/index.vue +51 -17
  10. package/chart/monitoring/prometheus/index.vue +37 -43
  11. package/chart/rancher-backup/index.vue +2 -1
  12. package/cloud-credential/azure.vue +4 -17
  13. package/components/Certificates.vue +164 -0
  14. package/components/CodeMirror.vue +19 -21
  15. package/components/CruResource.vue +1 -0
  16. package/components/EtcdInfoBanner.vue +1 -1
  17. package/components/ExplorerProjectsNamespaces.vue +25 -1
  18. package/components/IconOrSvg.vue +1 -1
  19. package/components/LandingPagePreference.vue +1 -4
  20. package/components/Questions/index.vue +1 -1
  21. package/components/ResourceDetail/Masthead.vue +16 -3
  22. package/components/ResourceTable.vue +14 -2
  23. package/components/ResourceYaml.vue +5 -0
  24. package/components/SideNav.vue +1 -1
  25. package/components/SingleClusterInfo.vue +1 -4
  26. package/components/Tabbed/index.vue +12 -0
  27. package/components/fleet/FleetRepos.vue +62 -27
  28. package/components/fleet/FleetResources.vue +6 -1
  29. package/components/form/ArrayListSelect.vue +10 -0
  30. package/components/form/KeyValue.vue +4 -0
  31. package/components/form/LabeledSelect.vue +4 -0
  32. package/components/formatter/Checked.vue +11 -3
  33. package/components/formatter/FleetClusterSummaryGraph.vue +27 -0
  34. package/components/formatter/FleetSummaryGraph.vue +23 -11
  35. package/components/formatter/LiveDuration.vue +1 -1
  36. package/components/formatter/PercentageBar.vue +1 -1
  37. package/components/formatter/__tests__/Checked.test.ts +19 -0
  38. package/components/nav/Group.vue +2 -2
  39. package/components/nav/Header.vue +0 -1
  40. package/components/nav/TopLevelMenu.vue +36 -6
  41. package/components/nav/Type.vue +1 -3
  42. package/components/nav/WindowManager/ContainerLogs.vue +101 -3
  43. package/components/nav/WindowManager/ContainerShell.vue +6 -1
  44. package/components/nav/WindowManager/__tests__/ContainerLogs.test.ts +186 -0
  45. package/components/nav/WindowManager/index.vue +11 -10
  46. package/components/nav/__tests__/TopLevelMenu.test.ts +33 -0
  47. package/components/nav/__tests__/Type.test.ts +1 -1
  48. package/components/nuxt/nuxt-child.js +14 -78
  49. package/components/nuxt/nuxt.js +1 -1
  50. package/{layouts → components/templates}/blank.vue +1 -1
  51. package/{layouts → components/templates}/default.vue +8 -98
  52. package/{layouts → components/templates}/error.vue +10 -19
  53. package/{layouts → components/templates}/home.vue +4 -1
  54. package/{layouts → components/templates}/plain.vue +4 -1
  55. package/{layouts → components/templates}/standalone.vue +1 -1
  56. package/{layouts → components/templates}/unauthenticated.vue +1 -1
  57. package/composables/useCompactInput.ts +20 -0
  58. package/composables/useLabeledFormElement.ts +138 -0
  59. package/config/harvester-manager-types.js +2 -0
  60. package/config/private-label.js +22 -0
  61. package/config/product/explorer.js +3 -0
  62. package/config/product/fleet.js +6 -1
  63. package/config/product/manager.js +8 -2
  64. package/config/query-params.js +1 -0
  65. package/config/router.js +385 -364
  66. package/config/settings.ts +1 -0
  67. package/config/store.js +1 -1
  68. package/config/system-namespaces.js +3 -0
  69. package/config/table-headers.js +47 -0
  70. package/core/plugin-routes.ts +56 -114
  71. package/core/plugin.ts +16 -10
  72. package/core/plugins-loader.js +7 -9
  73. package/core/plugins.js +0 -3
  74. package/creators/app/files/.gitlab-ci.yml +1 -1
  75. package/detail/fleet.cattle.io.cluster.vue +11 -1
  76. package/detail/provisioning.cattle.io.cluster.vue +4 -3
  77. package/dialog/ScaleMachineDownDialog.vue +34 -17
  78. package/edit/__tests__/service.test.ts +89 -0
  79. package/edit/auth/googleoauth.vue +1 -5
  80. package/edit/catalog.cattle.io.clusterrepo.vue +18 -0
  81. package/edit/cloudcredential.vue +2 -0
  82. package/edit/configmap.vue +2 -1
  83. package/edit/networking.k8s.io.networkpolicy/__tests__/PolicyRuleTarget.spec.ts +1 -1
  84. package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +15 -7
  85. package/edit/provisioning.cattle.io.cluster/__tests__/Advanced.test.ts +112 -0
  86. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.test.ts +473 -0
  87. package/edit/provisioning.cattle.io.cluster/__tests__/{CustomCommand.tests.ts → CustomCommand.test.ts} +4 -0
  88. package/edit/provisioning.cattle.io.cluster/__tests__/DrainOptions.test.ts +1 -1
  89. package/edit/provisioning.cattle.io.cluster/__tests__/index.test.ts +73 -0
  90. package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +7 -1
  91. package/edit/provisioning.cattle.io.cluster/__tests__/utils/cluster.ts +386 -0
  92. package/edit/provisioning.cattle.io.cluster/import.vue +2 -2
  93. package/edit/provisioning.cattle.io.cluster/index.vue +92 -36
  94. package/edit/provisioning.cattle.io.cluster/rke2.vue +171 -583
  95. package/edit/provisioning.cattle.io.cluster/tabs/AddOnConfig.vue +137 -0
  96. package/edit/provisioning.cattle.io.cluster/tabs/Advanced.vue +157 -0
  97. package/edit/provisioning.cattle.io.cluster/{Basics.vue → tabs/Basics.vue} +94 -19
  98. package/edit/provisioning.cattle.io.cluster/{MachinePool.vue → tabs/MachinePool.vue} +1 -0
  99. package/edit/provisioning.cattle.io.cluster/tabs/etcd/index.vue +135 -0
  100. package/edit/provisioning.cattle.io.cluster/tabs/networking/index.vue +189 -0
  101. package/edit/provisioning.cattle.io.cluster/tabs/registries/index.vue +144 -0
  102. package/edit/provisioning.cattle.io.cluster/tabs/upgrade/index.vue +76 -0
  103. package/edit/service.vue +12 -0
  104. package/edit/workload/mixins/workload.js +1 -1
  105. package/initialize/App.js +25 -71
  106. package/initialize/client.js +21 -162
  107. package/initialize/index.js +27 -123
  108. package/list/management.cattle.io.feature.vue +1 -7
  109. package/list/node.vue +1 -0
  110. package/machine-config/__tests__/vmwarevsphere.test.ts +100 -21
  111. package/machine-config/vmwarevsphere.vue +73 -51
  112. package/middleware/authenticated.js +10 -17
  113. package/mixins/auth-config.js +2 -7
  114. package/mixins/brand.js +29 -41
  115. package/mixins/labeled-form-element.ts +6 -1
  116. package/models/__tests__/management.cattle.io.node.ts +85 -0
  117. package/models/__tests__/management.cattle.io.nodepool.ts +83 -0
  118. package/models/__tests__/namespace.test.ts +49 -9
  119. package/models/__tests__/workload.test.ts +91 -0
  120. package/models/cluster/node.js +4 -4
  121. package/models/cluster.x-k8s.io.machinedeployment.js +14 -0
  122. package/models/fleet.cattle.io.cluster.js +4 -0
  123. package/models/fleet.cattle.io.gitrepo.js +56 -13
  124. package/models/management.cattle.io.kontainerdriver.js +1 -1
  125. package/models/management.cattle.io.node.js +18 -14
  126. package/models/management.cattle.io.nodepool.js +17 -0
  127. package/models/namespace.js +1 -1
  128. package/models/pod.js +20 -0
  129. package/models/provisioning.cattle.io.cluster.js +20 -3
  130. package/models/secret.js +117 -18
  131. package/models/workload.js +16 -0
  132. package/models/workload.service.js +18 -0
  133. package/package.json +10 -9
  134. package/pages/about.vue +0 -1
  135. package/pages/account/create-key.vue +0 -1
  136. package/pages/account/index.vue +0 -1
  137. package/pages/auth/login.vue +0 -1
  138. package/pages/auth/logout.vue +0 -2
  139. package/pages/auth/setup.vue +0 -4
  140. package/pages/auth/verify.vue +14 -8
  141. package/pages/c/_cluster/apps/charts/install.vue +4 -4
  142. package/pages/c/_cluster/apps/index.vue +0 -2
  143. package/pages/c/_cluster/auth/index.vue +0 -2
  144. package/pages/c/_cluster/ecm/index.vue +0 -2
  145. package/pages/c/_cluster/explorer/index.vue +28 -2
  146. package/pages/c/_cluster/fleet/index.vue +1 -1
  147. package/pages/c/_cluster/index.vue +0 -2
  148. package/pages/c/_cluster/settings/banners.vue +0 -2
  149. package/pages/c/_cluster/settings/brand.vue +0 -2
  150. package/pages/c/_cluster/settings/index.vue +0 -2
  151. package/pages/c/_cluster/settings/links.vue +0 -1
  152. package/pages/c/_cluster/settings/performance.vue +0 -1
  153. package/pages/c/_cluster/uiplugins/CatalogList/CatalogLoadDialog.vue +2 -1
  154. package/pages/c/_cluster/uiplugins/CatalogList/index.vue +10 -46
  155. package/pages/c/_cluster/uiplugins/index.vue +0 -2
  156. package/pages/diagnostic.vue +1 -2
  157. package/pages/fail-whale.vue +0 -1
  158. package/pages/prefs.vue +0 -1
  159. package/pages/support/index.vue +2 -8
  160. package/pkg/auto-import.js +1 -1
  161. package/plugins/axios.js +0 -36
  162. package/plugins/back-button.js +3 -5
  163. package/plugins/codemirror-loader.js +1 -1
  164. package/plugins/codemirror.js +41 -0
  165. package/plugins/dashboard-store/__tests__/{mutations.spec.ts → mutations.test.ts} +1 -1
  166. package/plugins/dashboard-store/__tests__/resource-class.test.ts +49 -0
  167. package/plugins/dashboard-store/__tests__/utils/store-mocks.ts +7 -0
  168. package/plugins/dashboard-store/actions.js +30 -4
  169. package/plugins/dashboard-store/classify.js +1 -18
  170. package/plugins/dashboard-store/getters.js +10 -5
  171. package/plugins/dashboard-store/index.js +0 -12
  172. package/plugins/dashboard-store/mutations.js +0 -4
  173. package/plugins/dashboard-store/resource-class.js +59 -18
  174. package/plugins/steve/__tests__/steve-class.spec.ts +59 -0
  175. package/plugins/steve/__tests__/utils/steve-mocks.ts +31 -0
  176. package/plugins/steve/getters.js +4 -1
  177. package/plugins/steve/norman-class.js +19 -0
  178. package/plugins/steve/steve-class.js +22 -0
  179. package/plugins/steve/subscribe.js +4 -10
  180. package/rancher-components/Accordion/Accordion.test.ts +45 -0
  181. package/rancher-components/Accordion/Accordion.vue +85 -0
  182. package/rancher-components/Accordion/index.ts +1 -0
  183. package/rancher-components/Form/LabeledInput/LabeledInput.test.ts +19 -2
  184. package/rancher-components/Form/LabeledInput/LabeledInput.vue +12 -1
  185. package/rancher-components/Form/Radio/RadioButton.test.ts +7 -3
  186. package/rancher-components/Form/Radio/RadioGroup.test.ts +30 -0
  187. package/rancher-components/Form/Radio/RadioGroup.vue +4 -0
  188. package/rancher-components/StringList/StringList.test.ts +270 -0
  189. package/rancher-components/StringList/StringList.vue +57 -18
  190. package/rancher-components/components/Accordion/Accordion.test.ts +45 -0
  191. package/rancher-components/components/Accordion/Accordion.vue +85 -0
  192. package/rancher-components/components/Accordion/index.ts +1 -0
  193. package/rancher-components/components/Form/LabeledInput/LabeledInput.test.ts +19 -2
  194. package/rancher-components/components/Form/LabeledInput/LabeledInput.vue +4 -1
  195. package/scripts/.gitlab/workflows/build-extension-catalog.gitlab-ci.yml +50 -0
  196. package/scripts/extension/parse-tag-name +2 -2
  197. package/scripts/publish-shell.sh +10 -0
  198. package/scripts/test-plugins-build.sh +85 -9
  199. package/server/har-file.js +183 -0
  200. package/store/catalog.js +1 -1
  201. package/store/features.js +1 -0
  202. package/store/i18n.js +11 -0
  203. package/store/index.js +10 -11
  204. package/store/prefs.js +33 -35
  205. package/store/type-map.js +8 -7
  206. package/tsconfig.json +35 -9
  207. package/tsconfig.paths.json +18 -0
  208. package/types/shell/index.d.ts +345 -214
  209. package/utils/__tests__/create-yaml.test.ts +60 -0
  210. package/utils/axios.js +0 -19
  211. package/utils/azure.js +24 -0
  212. package/utils/create-yaml.js +17 -10
  213. package/utils/monitoring.js +1 -1
  214. package/utils/nuxt.js +18 -39
  215. package/utils/object.js +14 -0
  216. package/utils/router.scrollBehavior.js +12 -14
  217. package/utils/time.js +1 -1
  218. package/utils/url.ts +1 -1
  219. package/vue.config.js +23 -2
  220. package/.DS_Store +0 -0
  221. package/assets/images/providers/aks-black.svg +0 -28
  222. package/assets/images/providers/aks.svg +0 -31
  223. package/edit/provisioning.cattle.io.cluster/__tests__/Basics.tests.ts +0 -234
  224. package/initialize/layouts.ts +0 -26
  225. package/mixins/fetch.server.js +0 -73
  226. package/pages/c/index.vue +0 -9
  227. package/pages/rio/mesh.vue +0 -508
  228. package/plugins/transitions.js +0 -4
  229. package/scripts/.DS_Store +0 -0
  230. package/scripts/verdaccio.log +0 -205
  231. package/tsconfig.default.json +0 -46
  232. package/yarn-error.log +0 -200
  233. /package/components/form/__tests__/{NameNsDescription.ts → NameNsDescription.test.ts} +0 -0
  234. /package/edit/networking.k8s.io.networkpolicy/__tests__/utils/{selectors.ts → selectors.test.ts} +0 -0
  235. /package/edit/provisioning.cattle.io.cluster/{AgentConfiguration.vue → tabs/AgentConfiguration.vue} +0 -0
  236. /package/edit/provisioning.cattle.io.cluster/{MemberRoles.vue → tabs/MemberRoles.vue} +0 -0
  237. /package/edit/provisioning.cattle.io.cluster/{S3Config.vue → tabs/etcd/S3Config.vue} +0 -0
  238. /package/edit/provisioning.cattle.io.cluster/{ACE.vue → tabs/networking/ACE.vue} +0 -0
  239. /package/edit/provisioning.cattle.io.cluster/{RegistryConfigs.vue → tabs/registries/RegistryConfigs.vue} +0 -0
  240. /package/edit/provisioning.cattle.io.cluster/{RegistryMirrors.vue → tabs/registries/RegistryMirrors.vue} +0 -0
  241. /package/edit/provisioning.cattle.io.cluster/{DrainOptions.vue → tabs/upgrade/DrainOptions.vue} +0 -0
  242. /package/plugins/dashboard-store/__tests__/{actions.spec.ts → actions.test.ts} +0 -0
  243. /package/plugins/dashboard-store/__tests__/{getters.spec.ts → getters.test.ts} +0 -0
@@ -2,7 +2,7 @@
2
2
  import { saveAs } from 'file-saver';
3
3
  import AnsiUp from 'ansi_up';
4
4
  import { addParams } from '@shell/utils/url';
5
- import { base64Decode } from '@shell/utils/crypto';
5
+ import { base64DecodeToBuffer } from '@shell/utils/crypto';
6
6
  import { LOGS_RANGE, LOGS_TIME, LOGS_WRAP } from '@shell/store/prefs';
7
7
  import LabeledSelect from '@shell/components/form/LabeledSelect';
8
8
  import { Checkbox } from '@components/Form/Checkbox';
@@ -25,6 +25,61 @@ import Window from './Window';
25
25
 
26
26
  let lastId = 1;
27
27
  const ansiup = new AnsiUp();
28
+ // Convert arrayBuffer(Uint8Array) to string
29
+ // ref: https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder
30
+ // ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of
31
+ const ab2str = (input, outputEncoding = 'utf8') => {
32
+ const decoder = new TextDecoder(outputEncoding);
33
+
34
+ return decoder.decode(input);
35
+ };
36
+
37
+ // The utf-8 encoded messages pushed by websocket may truncate multi-byte utf-8 characters,
38
+ // which causes the front-end to be unable to parse the truncated multi-byte utf-8 characters in the previous and next messages when decoding.
39
+ // Therefore, we need to determine whether the last 4 bytes of the current pushed message contain incomplete utf-8 encoded characters.
40
+ // ref: https://en.wikipedia.org/wiki/UTF-8#Encoding
41
+ const isLogTruncated = (uint8ArrayBuffer) => {
42
+ const len = uint8ArrayBuffer.length;
43
+ const count = Math.min(4, len);
44
+ let isTruncated = false;
45
+
46
+ // Parses the last ${count} bytes of the array to determine if there are any truncated utf-8 characters.
47
+ for ( let i = 0; i < count; i++ ) {
48
+ const a = uint8ArrayBuffer[len - (1 + i)];
49
+
50
+ // 1 byte utf-8 character in binary form: 0xxxxxxxxx
51
+ if ((a & 0b10000000) === 0b00000000) {
52
+ break;
53
+ }
54
+ // Multi-byte utf-8 character, intermediate binary form: 10xxxxxx
55
+ if ((a & 0b11000000) === 0b10000000) {
56
+ continue;
57
+ }
58
+ // 2 byte utf-8 character in binary form: 110xxxxx 10xxxxxx
59
+ if ((a & 0b11100000) === 0b11000000) {
60
+ if ( i !== 1) {
61
+ isTruncated = true;
62
+ }
63
+ break;
64
+ }
65
+ // 3 byte utf-8 character in binary form: 1110xxxx 10xxxxxx 10xxxxxx
66
+ if ((a & 0b11110000) === 0b11100000) {
67
+ if (i !== 2) {
68
+ isTruncated = true;
69
+ }
70
+ break;
71
+ }
72
+ // 4 byte utf-8 character in binary form: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
73
+ if ((a & 0b11111000) === 0b11110000) {
74
+ if (i !== 3) {
75
+ isTruncated = true;
76
+ }
77
+ break;
78
+ }
79
+ }
80
+
81
+ return isTruncated;
82
+ };
28
83
 
29
84
  export default {
30
85
  components: {
@@ -273,11 +328,54 @@ export default {
273
328
  console.error('Connect Error', e); // eslint-disable-line no-console
274
329
  });
275
330
 
331
+ let logBuffer = [];
332
+ let truncatedLog = '';
333
+
276
334
  this.socket.addEventListener(EVENT_MESSAGE, (e) => {
277
- const data = base64Decode(e.detail.data);
335
+ const decodedData = e.detail?.data || '';
336
+ const replacedData = decodedData.replace(/[-_]/g, (char) => char === '-' ? '+' : '/');
337
+ const b = base64DecodeToBuffer(replacedData);
338
+ const isTruncated = isLogTruncated(b);
339
+
340
+ if (isTruncated === true) {
341
+ logBuffer.push(...b);
342
+
343
+ return;
344
+ }
345
+
346
+ let d;
347
+
348
+ // If the logBuffer is not empty,
349
+ // there are truncated utf-8 characters in the previous message
350
+ // that need to be merged with the current message before decoding.
351
+ if (logBuffer.length > 0) {
352
+ // Convert arrayBuffer(Uint8Array) to string
353
+ // ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/of
354
+ d = ab2str(Uint8Array.of(...logBuffer, ...b));
355
+ logBuffer = [];
356
+ } else {
357
+ d = b.toString();
358
+ }
359
+ let data = d;
278
360
 
361
+ if (truncatedLog) {
362
+ data = `${ truncatedLog }${ d }`;
363
+ truncatedLog = '';
364
+ }
365
+
366
+ if (!d.endsWith('\n')) {
367
+ const lines = data.split(/\n/);
368
+
369
+ if (lines.length === 1) {
370
+ truncatedLog = data;
371
+
372
+ return;
373
+ }
374
+ data = lines.slice(0, -1).join('\n');
375
+ truncatedLog = lines.slice(-1);
376
+ }
279
377
  // Websocket message may contain multiple lines - loop through each line, one by one
280
- data.split('\n').forEach((line) => {
378
+ data.split('\n').filter((line) => line).forEach((line) => {
281
379
  let msg = line;
282
380
  let time = null;
283
381
 
@@ -79,6 +79,7 @@ export default {
79
79
  fitAddon: null,
80
80
  searchAddon: null,
81
81
  webglAddon: null,
82
+ canvasAddon: null,
82
83
  isOpen: false,
83
84
  isOpening: false,
84
85
  backlog: [],
@@ -155,6 +156,7 @@ export default {
155
156
  webgl: import(/* webpackChunkName: "xterm" */ 'xterm-addon-webgl'),
156
157
  weblinks: import(/* webpackChunkName: "xterm" */ 'xterm-addon-web-links'),
157
158
  search: import(/* webpackChunkName: "xterm" */ 'xterm-addon-search'),
159
+ canvas: import(/* webpackChunkName: "xterm" */ 'xterm-addon-canvas')
158
160
  });
159
161
 
160
162
  const terminal = new xterm.Terminal({
@@ -171,10 +173,11 @@ export default {
171
173
  this.searchAddon = new addons.search.SearchAddon();
172
174
 
173
175
  try {
174
- this.webglAddon = new addons.webgl.WebGlAddon();
176
+ this.webglAddon = new addons.webgl.WebglAddon();
175
177
  } catch (e) {
176
178
  // Some browsers (Safari) don't support the webgl renderer, so don't use it.
177
179
  this.webglAddon = null;
180
+ this.canvasAddon = new addons.canvas.CanvasAddon();
178
181
  }
179
182
 
180
183
  terminal.loadAddon(this.fitAddon);
@@ -184,6 +187,8 @@ export default {
184
187
 
185
188
  if (this.webglAddon) {
186
189
  terminal.loadAddon(this.webglAddon);
190
+ } else {
191
+ terminal.loadAddon(this.canvasAddon);
187
192
  }
188
193
 
189
194
  this.fit();
@@ -0,0 +1,186 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import ContainerLogs from '@shell/components/nav/WindowManager/ContainerLogs.vue';
3
+ import { base64Encode } from '@shell/utils/crypto';
4
+ import { Buffer } from 'buffer';
5
+ import { addEventListener } from '@shell/utils/socket';
6
+
7
+ jest.mock('@shell/utils/socket');
8
+
9
+ const getDefaultOptions = () => {
10
+ return {
11
+ propsData: {
12
+ tab: {},
13
+ active: true,
14
+ height: 100,
15
+ pod: {
16
+ spec: { nodeName: 'nodeId' },
17
+ links: { view: 'url' },
18
+ os: 'linux'
19
+ },
20
+ },
21
+ data() {
22
+ return { range: '30 minute' };
23
+ },
24
+ mocks: {
25
+ $store: {
26
+ getters: {
27
+ 'prefs/get': jest.fn(),
28
+ 'i18n/t': jest.fn(),
29
+ currentProduct: { inStore: 'cluster' }
30
+ }
31
+ }
32
+ }
33
+ };
34
+ };
35
+
36
+ describe('component: ContainerLogs', () => {
37
+ it('should receive messages correctly', async() => {
38
+ jest.clearAllMocks();
39
+ const wrapper = await shallowMount(ContainerLogs, getDefaultOptions());
40
+
41
+ const data1 = 'container logs test1\n';
42
+ const messageCallback = addEventListener.mock.calls.find(([e]) => e === 'message')[1];
43
+
44
+ messageCallback({ detail: { data: base64Encode(data1) } });
45
+
46
+ await wrapper.vm.$nextTick();
47
+ expect(wrapper.vm.backlog).toHaveLength(1);
48
+ expect(wrapper.vm.backlog[0].rawMsg).toBe(data1.trimEnd());
49
+ const data2 = 'container logs test2 中文日志内容测试\n';
50
+
51
+ messageCallback({ detail: { data: base64Encode(data2) } });
52
+ await wrapper.vm.$nextTick();
53
+ expect(wrapper.vm.backlog).toHaveLength(2);
54
+ expect(wrapper.vm.backlog[1].rawMsg).toBe(data2.trimEnd());
55
+ });
56
+
57
+ it('should not fail for an empty message/string', async() => {
58
+ jest.clearAllMocks();
59
+ const wrapper = await shallowMount(ContainerLogs, getDefaultOptions());
60
+
61
+ const data1 = '';
62
+ const messageCallback = addEventListener.mock.calls.find(([e]) => e === 'message')[1];
63
+
64
+ messageCallback({ detail: { data: base64Encode(data1) } });
65
+ await wrapper.vm.$nextTick();
66
+ expect(wrapper.vm.backlog).toHaveLength(0);
67
+ expect(wrapper.vm.filtered).toHaveLength(0);
68
+ });
69
+
70
+ it('should merge the message which be truncated line', async() => {
71
+ jest.clearAllMocks();
72
+ const wrapper = await shallowMount(ContainerLogs, getDefaultOptions());
73
+ const part1 = 'container logs part1';
74
+ const messageCallback = addEventListener.mock.calls.find(([e]) => e === 'message')[1];
75
+
76
+ messageCallback({ detail: { data: base64Encode(part1) } });
77
+ await wrapper.vm.$nextTick();
78
+
79
+ expect(wrapper.vm.backlog).toHaveLength(0);
80
+ const part2 = 'container logs part2\n';
81
+
82
+ messageCallback({ detail: { data: base64Encode(part2) } });
83
+ await wrapper.vm.$nextTick();
84
+ expect(wrapper.vm.backlog).toHaveLength(1);
85
+ expect(wrapper.vm.backlog[0].rawMsg).toBe(`${ part1 }${ part2 }`.trimEnd());
86
+ });
87
+
88
+ it('should merge truncated 2-byte utf-8 character messages', async() => {
89
+ jest.clearAllMocks();
90
+ const wrapper = await shallowMount(ContainerLogs, getDefaultOptions());
91
+ // Contains 2-byte utf-8 character message with one character truncation
92
+ const message = '¡¢£¤¥\n';
93
+ const arr = Buffer.from(message);
94
+
95
+ const part1 = arr.slice(0, 3).toString('base64');
96
+ const part2 = arr.slice(3).toString('base64');
97
+
98
+ const messageCallback = addEventListener.mock.calls.find(([e]) => e === 'message')[1];
99
+
100
+ messageCallback({ detail: { data: part1 } });
101
+ await wrapper.vm.$nextTick();
102
+ expect(wrapper.vm.backlog).toHaveLength(0);
103
+ messageCallback({ detail: { data: part2 } });
104
+ await wrapper.vm.$nextTick();
105
+ expect(wrapper.vm.backlog).toHaveLength(1);
106
+ expect(wrapper.vm.backlog[0].rawMsg).toBe(message.trimEnd());
107
+ });
108
+ it('should merge truncated 3-byte utf-8 character messages', async() => {
109
+ jest.clearAllMocks();
110
+ const wrapper = await shallowMount(ContainerLogs, getDefaultOptions());
111
+ // Contains 3-byte utf-8 character message with one character truncation
112
+ const message = 'ࠀࠁࠂࠃ\n';
113
+ const arr = Buffer.from(message);
114
+ // Truncate at the fourth byte
115
+ const part1 = arr.slice(0, 4).toString('base64');
116
+ const part2 = arr.slice(4).toString('base64');
117
+
118
+ const messageCallback = addEventListener.mock.calls.find(([e]) => e === 'message')[1];
119
+
120
+ messageCallback({ detail: { data: part1 } });
121
+ await wrapper.vm.$nextTick();
122
+ expect(wrapper.vm.backlog).toHaveLength(0);
123
+ messageCallback({ detail: { data: part2 } });
124
+ await wrapper.vm.$nextTick();
125
+ expect(wrapper.vm.backlog).toHaveLength(1);
126
+ expect(wrapper.vm.backlog[0].rawMsg).toBe(message.trimEnd());
127
+
128
+ // Truncate at the fifth byte
129
+ const part3 = arr.slice(0, 5).toString('base64');
130
+ const part4 = arr.slice(5).toString('base64');
131
+
132
+ messageCallback({ detail: { data: part3 } });
133
+ await wrapper.vm.$nextTick();
134
+ expect(wrapper.vm.backlog).toHaveLength(1);
135
+ messageCallback({ detail: { data: part4 } });
136
+ await wrapper.vm.$nextTick();
137
+ expect(wrapper.vm.backlog).toHaveLength(2);
138
+ expect(wrapper.vm.backlog[1].rawMsg).toBe(message.trimEnd());
139
+ });
140
+
141
+ it('should merge truncated 4-byte utf-8 character messages', async() => {
142
+ jest.clearAllMocks();
143
+ const wrapper = await shallowMount(ContainerLogs, getDefaultOptions());
144
+ // Contains 4-byte utf-8 character message with one character truncation
145
+ const message = '𐀀𐀁𐀂𐀃\n';
146
+ const arr = Buffer.from(message);
147
+
148
+ // Truncate at the fifth byte
149
+ const part1 = arr.slice(0, 5).toString('base64');
150
+ const part2 = arr.slice(5).toString('base64');
151
+
152
+ const messageCallback = addEventListener.mock.calls.find(([e]) => e === 'message')[1];
153
+
154
+ messageCallback({ detail: { data: part1 } });
155
+ await wrapper.vm.$nextTick();
156
+ expect(wrapper.vm.backlog).toHaveLength(0);
157
+ messageCallback({ detail: { data: part2 } });
158
+ await wrapper.vm.$nextTick();
159
+ expect(wrapper.vm.backlog).toHaveLength(1);
160
+ expect(wrapper.vm.backlog[0].rawMsg).toBe(message.trimEnd());
161
+
162
+ // Truncate at the sixth byte
163
+ const part3 = arr.slice(0, 6).toString('base64');
164
+ const part4 = arr.slice(6).toString('base64');
165
+
166
+ messageCallback({ detail: { data: part3 } });
167
+ await wrapper.vm.$nextTick();
168
+ expect(wrapper.vm.backlog).toHaveLength(1);
169
+ messageCallback({ detail: { data: part4 } });
170
+ await wrapper.vm.$nextTick();
171
+ expect(wrapper.vm.backlog).toHaveLength(2);
172
+ expect(wrapper.vm.backlog[1].rawMsg).toBe(message.trimEnd());
173
+
174
+ // Truncate at the seventh byte
175
+ const part5 = arr.slice(0, 7).toString('base64');
176
+ const part6 = arr.slice(7).toString('base64');
177
+
178
+ messageCallback({ detail: { data: part5 } });
179
+ await wrapper.vm.$nextTick();
180
+ expect(wrapper.vm.backlog).toHaveLength(2);
181
+ messageCallback({ detail: { data: part6 } });
182
+ await wrapper.vm.$nextTick();
183
+ expect(wrapper.vm.backlog).toHaveLength(3);
184
+ expect(wrapper.vm.backlog[2].rawMsg).toBe(message.trimEnd());
185
+ });
186
+ });
@@ -20,10 +20,6 @@ export default {
20
20
 
21
21
  height: {
22
22
  get() {
23
- if ( process.server ) {
24
- return 0;
25
- }
26
-
27
23
  if ( this.userHeight ) {
28
24
  return this.userHeight;
29
25
  }
@@ -52,10 +48,6 @@ export default {
52
48
 
53
49
  width: {
54
50
  get() {
55
- if ( process.server ) {
56
- return 0;
57
- }
58
-
59
51
  if (this.userWidth) {
60
52
  return this.userWidth;
61
53
  }
@@ -342,7 +334,8 @@ export default {
342
334
  />
343
335
  <span class="tab-label"> {{ tab.label }}</span>
344
336
  <i
345
- class="closer icon icon-fw icon-x"
337
+ data-testid="wm-tab-close-button"
338
+ class="closer icon icon-fw icon-x wm-closer-button"
346
339
  @click.stop="close(tab.id)"
347
340
  />
348
341
  </div>
@@ -440,9 +433,16 @@ export default {
440
433
  margin-left: 5px;
441
434
  border: 1px solid var(--body-text);
442
435
  border-radius: var(--border-radius);
436
+ line-height: 12px;
437
+ font-size: 10px;
438
+ width: 14px;
439
+ align-self: center;
440
+ display: flex;
441
+ justify-content: center;
443
442
 
444
443
  &:hover {
445
- background-color: var(--wm-closer-hover-bg);
444
+ border-color: var(--link-border);
445
+ color: var(--link-border);
446
446
  }
447
447
  }
448
448
  }
@@ -502,4 +502,5 @@ export default {
502
502
  border-right: var(--nav-border-size) solid var(--nav-border);
503
503
  }
504
504
  }
505
+
505
506
  </style>
@@ -1,5 +1,6 @@
1
1
  import { mount, Wrapper } from '@vue/test-utils';
2
2
  import TopLevelMenu from '@shell/components/nav/TopLevelMenu';
3
+ import { SETTING } from '@shell/config/settings';
3
4
 
4
5
  // DISCLAIMER: This should not be added here, although we have several store requests which are irrelevant
5
6
  const defaultStore = {
@@ -32,6 +33,38 @@ describe('topLevelMenu', () => {
32
33
  expect(cluster.exists()).toBe(true);
33
34
  });
34
35
 
36
+ it('should not "crash" the component if the structure of banner settings is in an old format', () => {
37
+ const wrapper: Wrapper<InstanceType<typeof TopLevelMenu>> = mount(TopLevelMenu, {
38
+ mocks: {
39
+ $store: {
40
+ getters: {
41
+ 'management/all': () => [{ name: 'whatever' },
42
+ // object based on https://github.com/rancher/dashboard/issues/10140#issuecomment-1883252402
43
+ {
44
+ id: SETTING.BANNERS,
45
+ value: JSON.stringify({
46
+ banner: {
47
+ color: '#78c9cf',
48
+ background: '#27292e',
49
+ text: 'Hello World!'
50
+ },
51
+ showHeader: 'true',
52
+ showFooter: 'true'
53
+ })
54
+ }],
55
+ ...defaultStore
56
+ },
57
+ },
58
+ },
59
+ stubs: ['BrandImage', 'nuxt-link']
60
+ });
61
+
62
+ expect(wrapper.vm.globalBannerSettings).toStrictEqual({
63
+ headerFont: '2em',
64
+ footerFont: '2em'
65
+ });
66
+ });
67
+
35
68
  describe('searching a term', () => {
36
69
  describe('should displays a no results message if have clusters but', () => {
37
70
  it('given no matching clusters', () => {
@@ -5,7 +5,7 @@ import Type from '@shell/components/nav/Type.vue';
5
5
  jest.mock('vue-router');
6
6
 
7
7
  // Configuration text
8
- const className = 'nuxt-link-active';
8
+ const className = 'router-link-active';
9
9
 
10
10
  describe('component: Type', () => {
11
11
  describe('should not use highlight class', () => {
@@ -18,10 +18,8 @@ export default {
18
18
 
19
19
  data.nuxtChild = true
20
20
  const _parent = parent
21
- const transitions = parent.$nuxt.nuxt.transitions
22
- const defaultTransition = parent.$nuxt.nuxt.defaultTransition
23
-
24
21
  let depth = 0
22
+
25
23
  while (parent) {
26
24
  if (parent.$vnode && parent.$vnode.data.nuxtChild) {
27
25
  depth++
@@ -29,48 +27,18 @@ export default {
29
27
  parent = parent.$parent
30
28
  }
31
29
  data.nuxtChildDepth = depth
32
- const transition = transitions[depth] || defaultTransition
33
- const transitionProps = {}
34
- transitionsKeys.forEach((key) => {
35
- if (typeof transition[key] !== 'undefined') {
36
- transitionProps[key] = transition[key]
37
- }
38
- })
39
30
 
40
31
  const listeners = {}
41
- listenersKeys.forEach((key) => {
42
- if (typeof transition[key] === 'function') {
43
- listeners[key] = transition[key].bind(_parent)
44
- }
45
- })
46
- if (process.client) {
47
- // Add triggerScroll event on beforeEnter (fix #1376)
48
- const beforeEnter = listeners.beforeEnter
49
- listeners.beforeEnter = (el) => {
50
- // Ensure to trigger scroll event after calling scrollBehavior
51
- window.$nuxt.$nextTick(() => {
52
- window.$nuxt.$emit('triggerScroll')
53
- })
54
- if (beforeEnter) {
55
- return beforeEnter.call(_parent, el)
56
- }
57
- }
58
- }
59
-
60
- // make sure that leave is called asynchronous (fix #5703)
61
- if (transition.css === false) {
62
- const leave = listeners.leave
63
-
64
- // only add leave listener when user didnt provide one
65
- // or when it misses the done argument
66
- if (!leave || leave.length < 2) {
67
- listeners.leave = (el, done) => {
68
- if (leave) {
69
- leave.call(_parent, el)
70
- }
71
-
72
- _parent.$nextTick(done)
73
- }
32
+
33
+ // Add triggerScroll event on beforeEnter (fix #1376)
34
+ const beforeEnter = listeners.beforeEnter
35
+ listeners.beforeEnter = (el) => {
36
+ // Ensure to trigger scroll event after calling scrollBehavior
37
+ window.$nuxt.$nextTick(() => {
38
+ window.$nuxt.$emit('triggerScroll')
39
+ })
40
+ if (beforeEnter) {
41
+ return beforeEnter.call(_parent, el)
74
42
  }
75
43
  }
76
44
 
@@ -80,43 +48,11 @@ export default {
80
48
  routerView = h('keep-alive', { props: props.keepAliveProps }, [routerView])
81
49
  }
82
50
 
51
+ // this needs to be a "transition" or another non-rendering component,
52
+ // otherwise we will break pages like the charts wizard or the extensions main screen (DOM would render an additional element and break CSS)
53
+ // we can deal with this later once we remove this component and <nuxt /> component
83
54
  return h('transition', {
84
- props: transitionProps,
85
55
  on: listeners
86
56
  }, [routerView])
87
57
  }
88
58
  }
89
-
90
- const transitionsKeys = [
91
- 'name',
92
- 'mode',
93
- 'appear',
94
- 'css',
95
- 'type',
96
- 'duration',
97
- 'enterClass',
98
- 'leaveClass',
99
- 'appearClass',
100
- 'enterActiveClass',
101
- 'enterActiveClass',
102
- 'leaveActiveClass',
103
- 'appearActiveClass',
104
- 'enterToClass',
105
- 'leaveToClass',
106
- 'appearToClass'
107
- ]
108
-
109
- const listenersKeys = [
110
- 'beforeEnter',
111
- 'enter',
112
- 'afterEnter',
113
- 'enterCancelled',
114
- 'beforeLeave',
115
- 'leave',
116
- 'afterLeave',
117
- 'leaveCancelled',
118
- 'beforeAppear',
119
- 'appear',
120
- 'afterAppear',
121
- 'appearCancelled'
122
- ]
@@ -1,7 +1,7 @@
1
1
  import Vue from 'vue'
2
2
  import { compile } from '../../utils/nuxt'
3
3
 
4
- import NuxtError from '../../layouts/error.vue'
4
+ import NuxtError from '../../components/templates/error.vue'
5
5
 
6
6
  import NuxtChild from './nuxt-child'
7
7
 
@@ -11,7 +11,7 @@ export default {
11
11
 
12
12
  <template>
13
13
  <main class="main-layout">
14
- <nuxt />
14
+ <router-view :key="$route.path" />
15
15
 
16
16
  <Inactivity />
17
17
  </main>