10
10
class =" isolate"
11
11
/>
12
12
<template
13
- v-for =" titlePart , index in failedTestData .mappedTitleParts "
13
+ v-for =" { text , type } , index in failedTestData .mappedTitleParts "
14
14
:key =" ` ${titlePart }-${index } ` "
15
15
:data-cy =" ` titleParts-${index } ` "
16
16
>
17
17
<IconChevronRightSmall
18
- v-if =" index !== 0 && titlePart. type !== 'LAST-1 '"
18
+ v-if =" index !== 0 && type !== 'LAST-PART-END '"
19
19
:data-cy =" `titleParts-${index}-chevron`"
20
20
size =" 8"
21
21
stroke-color =" gray-200"
22
22
fill-color =" gray-200"
23
23
class =" shrink-0"
24
- :class =" titlePart. type === 'MIDDLE' ? 'hidden lg:block' : titlePart. type === 'ELLIPSIS' ? 'lg:hidden' : ''"
24
+ :class =" type === 'MIDDLE' ? 'hidden lg:block' : type === 'ELLIPSIS' ? 'lg:hidden' : ''"
25
25
/>
26
26
<span
27
27
:data-cy =" `titleParts-${index}-title`"
28
- :class =" titlePart. type === 'ELLIPSIS' ? 'px-2.5 shrink-0 lg:hidden' :
29
- titlePart. type === 'MIDDLE' ? 'truncate px-2.5 hidden lg:block' :
30
- titlePart. type === 'LAST-1 ' ? 'shrink-0 whitespace-pre' :
31
- titlePart. type === 'LAST-0 ' ? 'pl-2.5 truncate' : 'px-2.5 truncate'"
28
+ :class =" type === 'ELLIPSIS' ? 'px-2.5 shrink-0 lg:hidden' :
29
+ type === 'MIDDLE' ? 'truncate px-2.5 hidden lg:block' :
30
+ type === 'LAST-PART-END ' ? 'shrink-0 whitespace-pre' :
31
+ type === 'LAST-PART-START ' ? 'pl-2.5 truncate' : 'px-2.5 truncate'"
32
32
>
33
- {{ titlePart.title }}
33
+ <template v-if =" type === ' ELLIPSIS' " >
34
+ <Tooltip >
35
+ <!-- button gives us free keyboard focus activation of the tooltip -->
36
+ <button >{{ text }}</button >
37
+ <span class =" sr-only" >{{ middlePartText }}</span >
38
+ <template #popper >
39
+ <span data-cy =" tooltip-content" >{{ middlePartText }}</span >
40
+ </template >
41
+ </Tooltip >
42
+ </template >
43
+ <template v-else >
44
+ {{ text }}
45
+ </template >
34
46
</span >
35
47
</template >
36
48
<div
@@ -68,6 +80,7 @@ import DebugArtifactLink from './DebugArtifactLink.vue'
68
80
import GroupedDebugFailedTestVue from ' ./GroupedDebugFailedTest.vue'
69
81
import { computed } from ' vue'
70
82
import type { StatsMetadata_GroupsFragment } from ' ../generated/graphql'
83
+ import Tooltip from ' @packages/frontend-shared/src/components/Tooltip.vue'
71
84
import { getDebugArtifacts } from ' ./utils/debugArtifacts'
72
85
import type { TestResults } from ' ./DebugSpec.vue'
73
86
import { useI18n } from ' @cy/i18n'
@@ -80,61 +93,79 @@ const props = defineProps<{
80
93
expandable: boolean
81
94
}>()
82
95
96
+ type ItemType = ' SHOW_FULL' | ' MIDDLE' | ' ELLIPSIS' | ' LAST-PART-START' | ' LAST-PART-END'
97
+ type MappedTitlePart = {
98
+ text: string
99
+ type: ItemType
100
+ }
101
+
83
102
const failedTestData = computed (() => {
84
103
const runInstance = props .failedTestsResult [0 ].instance
85
104
const titleParts = props .failedTestsResult [0 ].titleParts
86
105
87
- type Parts = ' FIRST' | ' MIDDLE' | ' PENULTIMATE' | ' ELLIPSIS' | ' LAST-0' | ' LAST-1'
88
- type MappedTitlePart = {
89
- title: string
90
- type: Parts
91
- }
92
106
let isFirstMiddleAdded: boolean = false
93
- const mappedTitleParts: MappedTitlePart [] = titleParts .map <MappedTitlePart | MappedTitlePart []>((ele , index , parts ) => {
107
+
108
+ const mappedTitleParts: MappedTitlePart [] = titleParts .map <MappedTitlePart | MappedTitlePart []>((titlePart , index , parts ) => {
94
109
if (index === 0 ) {
110
+ // always use the whole first part of the title
95
111
return {
96
- title: ele ,
97
- type: ' FIRST ' ,
112
+ text: titlePart ,
113
+ type: ' SHOW_FULL ' ,
98
114
}
99
115
}
100
116
101
117
if (index === parts .length - 1 ) {
118
+ // split the last part into 2 pieces, so that we can truncate the first half if needed,
119
+ // and still show the end of the text.
102
120
return [
103
121
{
104
- title: ele .slice (0 , ele .length - 15 ),
105
- type: ' LAST-0 ' ,
122
+ text: titlePart .slice (0 , titlePart .length - 15 ),
123
+ type: ' LAST-PART-START ' ,
106
124
},
107
125
{
108
- title: ele .slice (ele .length - 15 ),
109
- type: ' LAST-1 ' ,
126
+ text: titlePart .slice (titlePart .length - 15 ),
127
+ type: ' LAST-PART-END ' ,
110
128
},
111
129
]
112
130
}
113
131
114
132
if (index === parts .length - 2 && parts .length >= 3 ) {
133
+ // this is the second-last part of the title,
134
+ // and will be the *third-to-last* item in the array.
135
+
136
+ // We only label this SHOW_FULL if there are enough
137
+ // actual parts in the title that it is required to separate this
138
+ // from "middle" items that may be hidden in smaller screens
139
+
115
140
return {
116
- title: ele ,
117
- type: ' PENULTIMATE ' ,
141
+ text: titlePart ,
142
+ type: ' SHOW_FULL ' ,
118
143
}
119
144
}
120
145
121
146
if (! isFirstMiddleAdded && parts .length > 3 ) {
122
147
isFirstMiddleAdded = true
123
148
149
+ // a fake part with type ELLIPSIS is shown conditionally
150
+ // at smaller screen sizes
151
+
152
+ // we insert it here and will flatten the result later
153
+ // to undo the nesting this creates
124
154
return [
125
155
{
126
- title : ' ...' ,
156
+ text : ' ...' ,
127
157
type: ' ELLIPSIS' ,
128
158
},
129
159
{
130
- title: ele ,
160
+ text: titlePart ,
131
161
type: ' MIDDLE' ,
132
162
},
133
163
]
134
164
}
135
165
136
- return { title: ele , type: ' MIDDLE' }
137
- }).flat ()
166
+ return { text: titlePart , type: ' MIDDLE' }
167
+ })
168
+ .flat () // flatten the array since one of the internal items may itself be an array.
138
169
139
170
const debugArtifacts = getDebugArtifacts (runInstance , t )
140
171
@@ -144,6 +175,13 @@ const failedTestData = computed(() => {
144
175
}
145
176
})
146
177
178
+ const middlePartText = computed (() => {
179
+ return failedTestData .value .mappedTitleParts
180
+ .filter ((item ) => item .type === ' MIDDLE' )
181
+ .map ((item ) => item .text )
182
+ .join (' > ' )
183
+ })
184
+
147
185
</script >
148
186
<style scoped>
149
187
[data-cy = test-group ]:hover .test-row-artifacts , [data-cy = test-group ]:focus-within .test-row-artifacts {
0 commit comments