What
BlastForm.selectedChannel is treated as a plain string by every consumer, but bound to an NcSelect whose options are {value, label} objects. The moment that model becomes an object, four things break at once and canSubmit() goes false, which is what the surviving requestConsent e2e failure reports.
The inconsistency
src/views/blasts/BlastForm.vue:
// 223 — initialised as a STRING
selectedChannel: 'email',
// 271 — options are OBJECTS
channelOptions() {
return [
{ value: 'email', label: this.t('pipelinq', 'Email') },
{ value: 'sms', label: this.t('pipelinq', 'SMS') },
]
},
<!-- 68 — bound with label="label", so vue-select hands back the OBJECT -->
<NcSelect v-model="selectedChannel" :options="channelOptions" label="label" :clearable="false" />
Every read assumes the string:
| line |
code |
breaks as an object |
| 287 |
this.templates.filter((t) => t.channel === this.selectedChannel) |
matches nothing, so the template list empties |
| 456 |
this.selectedChannel !== 'email' |
always true, so the preflight is skipped |
| 464 |
channel: this.selectedChannel |
posts an object where the API expects "email" |
| 517 |
params: { channel: this.selectedChannel } |
same, as a query param |
| 323 |
!!this.selectedChannel |
still truthy, so canSubmit() gives no warning |
Line 323 is why this is quiet: the guard that would catch it only checks truthiness, and an object is truthy.
How it shows up
#1707 observed it from the outside and wrote the warning into the test: "opening it and dismissing it without picking clears the model, which makes canSubmit() false and leaves Create blast permanently disabled. Tried, measured, reverted."
That PR is merged and its serial-mode fix worked. skipAndSend passes now, and requestConsent fails fast with a real message instead of a 120s timeout:
Error: "Create blast" is disabled: canSubmit() is false, so one of
name / segment / template / channel is unset even though every step advanced
33 × locator resolved to <button disabled ...>
Run 33569048537, development at 4d4338b9: 4 failed / 328 passed. This is one of the four.
Fix
Two options, and they are not equivalent.
- Keep the model a string and let the select map to it —
:reduce="(o) => o.value" — so the four call sites above stay correct as written. Smallest change, matches every existing consumer.
- Make the model an object and update all five reads plus the initial value. Larger, and line 464/517 then need
.value before hitting the API.
(1) looks right. Whichever is chosen, canSubmit()'s channel term should assert the shape rather than truthiness, or this returns silently the next time.
Not claimed
I have not run the wizard interactively. The code inconsistency above is objective, and the failure mode matches what #1707 measured, but the exact moment the model flips (mount, open-and-dismiss, or advance) is worth confirming before fixing.
What
BlastForm.selectedChannelis treated as a plain string by every consumer, but bound to anNcSelectwhose options are{value, label}objects. The moment that model becomes an object, four things break at once andcanSubmit()goes false, which is what the survivingrequestConsente2e failure reports.The inconsistency
src/views/blasts/BlastForm.vue:Every read assumes the string:
this.templates.filter((t) => t.channel === this.selectedChannel)this.selectedChannel !== 'email'channel: this.selectedChannel"email"params: { channel: this.selectedChannel }!!this.selectedChannelcanSubmit()gives no warningLine 323 is why this is quiet: the guard that would catch it only checks truthiness, and an object is truthy.
How it shows up
#1707observed it from the outside and wrote the warning into the test: "opening it and dismissing it without picking clears the model, which makes canSubmit() false and leaves Create blast permanently disabled. Tried, measured, reverted."That PR is merged and its serial-mode fix worked.
skipAndSendpasses now, andrequestConsentfails fast with a real message instead of a 120s timeout:Run 33569048537,
developmentat4d4338b9: 4 failed / 328 passed. This is one of the four.Fix
Two options, and they are not equivalent.
:reduce="(o) => o.value"— so the four call sites above stay correct as written. Smallest change, matches every existing consumer..valuebefore hitting the API.(1) looks right. Whichever is chosen,
canSubmit()'s channel term should assert the shape rather than truthiness, or this returns silently the next time.Not claimed
I have not run the wizard interactively. The code inconsistency above is objective, and the failure mode matches what #1707 measured, but the exact moment the model flips (mount, open-and-dismiss, or advance) is worth confirming before fixing.