Skip to content

feat: upgrade Apollo Client 3 to 4 - #4144

Draft
akanshaaa19 wants to merge 24 commits into
masterfrom
apollo-client-v4-upgrade
Draft

akanshaaa19 wants to merge 24 commits into
masterfrom
apollo-client-v4-upgrade

Conversation

@akanshaaa19

Copy link
Copy Markdown
Member

Summary

Upgrades @apollo/client from 3.x to 4.2.12, closing out issue #3688's final checklist item. Builds on the already-merged onCompleted/onError cleanup batches (#4132, #4133, #4134).

  • Rewrote src/config/apolloclient.ts's link chain natively (v4 dropped support for apollo-link-token-refresh and apollo-absinthe-upload-link, both unmaintained with no v4-compatible releases): native upload link (ported extractFiles from apollo-absinthe-upload-link), native token-refresh link built on the existing TokenRenewalManager singleton, and v4's own SetContextLink/ErrorLink classes in place of @apollo/link-context/@apollo/link-error.
  • src/config/gql.ts wraps gql as TypedDocumentNode<any, any> so untyped GraphQL documents (this codebase has no codegen) don't collapse useQuery/useMutation result types to {} under v4's stricter default generics — restores v3's actual (implicit any) behavior rather than introducing new type safety. Proper per-query typed documents would be a separate, much larger initiative (codegen + schema introspection) and is intentionally out of scope here.
  • Configured LocalState (both on the real Apollo client and via MockedProvider's defaultProps in test setup) — v4 throws once a query containing @client fields (NOTIFICATION, ERROR_MESSAGE, SEARCH_OFFSET, SCROLL_HEIGHT) misses the cache, unless LocalState is present. This app never used local resolvers (writes @client fields directly via cache.writeQuery), so a resolver-less LocalState is enough.
  • Migrated remaining onCompleted/onError usages that batches 3-5 didn't cover (v4 dropped both from useQuery/useLazyQuery; kept for useMutation) and moved useLazyQuery's variables from hook options to the execute() call.
  • Fixed the official codemod's mechanical fallout: ~245 files' hook imports moved to @apollo/client/react, MockedProvider imports moved to @apollo/client/testing/react, addTypename removed from ~94 test files.

Real bugs fixed along the way (not just migration noise)

  • App.tsx: v4 skips the entire custom link chain (including refreshTokenLink) for queries that are 100% @client fields, so the proactive token-refresh check that used to piggyback on ErrorHandler's query needed its own mount effect.
  • Billing.tsx, FlowTranslation.tsx, CollectionList.tsx: const { data, error } = await mutate() assumed GraphQL errors resolve with an error field; v4's default errorPolicy rejects the promise instead, so these silently swallowed failures until wrapped in try/catch.
  • common/notification.ts: setErrorMessage read error.networkError/.graphQLErrors, which no longer exist now that ApolloError is gone — now checks CombinedGraphQLErrors.is(error).
  • List.tsx: the row-fetch effect depended on the filters prop by reference; several callers pass a fresh object literal every render, which under v4's stricter fetchMore/cache-only rules turned into runaway refetches. Now keyed on JSON.stringify(filters).
  • ChatMessages.tsx: switched its search query from cache-only to cache-first, since v4 no longer allows fetchMore on a cache-only query.
  • Found and fixed a pre-existing, unrelated-to-this-migration bug: 17 test/mock files used a variableMatcher field on MockedResponse that was never real Apollo API (silently ignored) — the real API is request.variables as a matcher function.

Test infrastructure changes

  • src/setupTests.ts: MockLink.defaultOptions.delay = 1 — v4's MockedProvider defaults to a randomized "realistic" network delay instead of resolving on the next macrotask, which broke synchronous assertions across ~30 test files expecting near-immediate mock resolution.

Test plan

  • tsc --noEmit clean (was 892 errors mid-migration)
  • yarn format / prettier clean
  • Full vitest run green, verified stable across repeated runs (1828/1828 non-skipped tests passing)
  • yarn dev boots and serves correctly
  • Manual/E2E verification of the rewritten apolloclient.ts (auth token refresh, file upload, WebSocket subscriptions, error handling) against a live backend — recommend running the e2e-test-engineer suite before merge, since this touches security-sensitive auth/token logic that can't be fully verified by unit tests alone

🤖 Generated with Claude Code

akanshaaa19 and others added 18 commits August 12, 2026 17:16
…tch 3)

Follows Apollo's deprecation of onCompleted/onError on query and mutation
hooks. Migrates FormLayout/List consumers and templates to await the
mutate/query call directly and derive query state via useEffect.

Files: TemplateOptions, RaiseToGupShup, FlowEditor, FlowTranslation,
InteractiveMessage, TranslateButton, KnowledgeBaseOptions (AssistantOptions),
CollectionList. PromptGeneratorModal (CreateAssistant), AssistantList, and
Organization were already compliant.

Closes #3953
…tch 4)

Follows Apollo's deprecation of onCompleted/onError on query and mutation
hooks, and on per-call mutate() options. Migrates Settings and WA Groups
consumers to await the mutate/query call directly and derive query state
via useEffect.

Files: MyAccount, Billing, WhatsAppFormList, Configure, VersionHistory,
GroupCollectionList, WaPollsList, GroupDetails. Organization and
WaManagedPhones were already compliant.

Closes #3954
…tch 5)

Follows Apollo's deprecation of onCompleted/onError on query and mutation
hooks. Migrates the remaining consumers to await the mutate/query call
directly and derive query state via useEffect.

Files: NotificationList, BlockContactList, ContactFieldList,
AdminContactManagement, UploadContactsDialog, SavedSearchToolbar,
OrganizationList, SheetIntegrationList, BulkAction, ExportTicket,
ExportConsulting, CollectionContactList.

GroupMessageSubscription and WalletBalance were flagged in the issue but
only use subscribeToMore's own onError (a distinct, non-deprecated Apollo
API) — no changes needed there. The "Upgrade Apollo Client to target
version" checklist item is a separate major-version migration (3.x -> 4.x)
and is out of scope for this callback-removal PR.

Closes #3955
Adds tests for previously-untested success/error branches introduced by
replacing onCompleted/onError with async/await (getFreeFlow success path,
export/reset/publish flow error and success paths, file upload success and
failure, auto-translate success/trim-warning, createKnowledgeBase success).

Also fixes two pre-existing test-suite footguns surfaced while writing these:
- FlowTranslation.test.tsx and KnowledgeBaseOptions.test.tsx held module-level
  Apollo/notification spies that leaked call history across tests (or got
  silently detached by an earlier vi.restoreAllMocks()), letting several
  tests pass without actually exercising the code they claimed to cover.
- InteractiveMessage.test.tsx's file-upload test never triggered a real
  file-change event; replaced with a reliable direct input-change trigger.
- FlowTranslation.tsx's ImportButton flow is now exercised via a mocked
  ImportButton instead of relying on FileReader completion in jsdom.

Simplifies a few call sites that were checking `data?.` after mutations that
either resolve with data or throw (never resolve with `data` undefined),
matching the original onCompleted destructuring and removing unreachable
branches.
…tch 4)

Adds tests for previously-untested success/error branches introduced by
replacing onCompleted/onError with async/await (password update
success/failure, subscription 3D-secure failure and unexpected mutation
failure, customer-portal redirect, form activate success/failure, poll
delete failure, form-revert failure, form-publish failure).

Also fixes a real regression from the original refactor: the "pending"
state's "Visit Stripe portal" button still called the raw getCustomerPortal
lazy query instead of the new visitCustomerPortal handler, so window.open
never fired for that button.

Restructures the Billing test's Stripe confirmCardSetup mock into a shared
vi.fn() so individual tests can override its resolved value (previously it
was a fresh closure per render, making the 3D-secure failure path
untestable), and strengthens two pre-existing weak tests (`open customer
portal`, `subscription status is already in pending state`) that clicked
buttons but asserted nothing.
Add tests for previously-unexercised error/edge paths introduced by the
onCompleted/onError -> await+try/catch migration: network-error handling
in ContactFieldList, AdminContactManagement, UploadContactsDialog,
NotificationList, OrganizationList, and SheetIntegrationList.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
stripe is already narrowed non-null by the early return a few lines
above (if (!stripe || !elements) return;), so the inner if (stripe)
branch was always true and flagged by static analysis as dead code.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- InteractiveMessage.tsx: fix uploadingFile flag stuck true after a
  successful upload (missing finally reset), surface real errors via
  setErrorMessage, translate success message
- FlowTranslation.tsx: check importFlowLocalization.success before
  closing the import dialog/reloading the editor; report exceptions
  via setErrorMessage
- CollectionList.tsx: fix "occured" -> "occurred" typo
- RaiseToGupShup.tsx: wrap Yup validation messages with t()
- TranslateButton.tsx: wrap success notifications with t()
- FlowEditor.test.tsx: actually await the findByText lookup instead of
  asserting on an unawaited promise
- InteractiveMessage.test.tsx: strengthen upload success/failure tests
  to assert on real UI state, not just that a notification fired; fix
  a mismatched mock that was silently falling through to a generic
  "no mock" rejection

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- MyAccount.tsx: apply the interface-language change only after the
  update-language mutation actually succeeds, instead of optimistically
  switching i18n/cache and then finding out the server rejected it
- Billing.tsx: pass 'noopener' to the customer-portal window.open; use
  the just-created paymentMethod.id for 3D-secure confirmation instead
  of the stale paymentMethodId state (set one render too late); drop
  the now-dead paymentMethodId state entirely
- GroupCollectionList.tsx: wrap the group-removal mutation in try/catch
  so a rejected mutation is surfaced via setErrorMessage instead of
  silently closing the dialog with no feedback; only close the dialog
  and reset selection on success so the user can retry on failure
- Configure.tsx / VersionHistory.tsx / WhatsAppFormList.tsx: check the
  `errors` payload on publish/save/revert/activate mutations before
  treating them as successful (these mutations return errors inside
  resolved data, not as thrown exceptions)
- mocks/WhatsAppForm.tsx: fix the activate-success mock returning
  'INACTIVE' instead of 'PUBLISHED', which let a broken response shape
  pass as success

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- BlockContactList.tsx, CollectionContactList.tsx: wrap the
  unblock/remove-from-collection mutations in try/catch and route
  failures through setErrorMessage instead of letting a rejected
  mutation silently fall through
- OrganizationList.tsx: check updateOrganizationStatus.errors before
  showing a success toast, and report rejected mutations
- BulkAction.tsx: check updateTicketStatusBasedOnTopic.success before
  closing the dialog, and report rejected mutations
- AdminContactManagement.tsx: fix moveContactsErrors truthy-on-empty-
  array check (an empty errors array read as a failure and skipped
  setShowStatus/left the upload button stuck loading)
- UploadContactsDialog.tsx: fix reading errors from data.errors[0]
  instead of the actual data.importContacts.errors, which made a
  returned import error throw and fall into the generic catch instead
  of showing the real message; clear uploadingContacts on that path
- NotificationList.tsx: convert the contact-upload-report download to
  async/await + try/catch (was still using .then/.catch)
- ContactFieldList.tsx, NotificationList.tsx: wrap remaining raw
  notification strings with t()
- Strengthen two tests (ContactFieldList, UploadContactsDialog) that
  were asserting on state already true before the mutation resolved,
  so a regression in the failure handler wouldn't have failed them

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- MyAccount.tsx: don't let a successful language-change mutation reset
  an in-progress password-change flow. setShowOTPButton(true) had
  moved into the shared success helper in the previous fix, so
  changing language while the OTP/password fields were open would
  incorrectly hide them. Moved it back into the password-specific
  success path only.
- Billing.tsx: visitCustomerPortal ignored the lazy query's error
  field entirely, giving no feedback and never opening the portal on
  failure; now checks `error` and `data?.customerPortal?.url` and
  reports failures via setNotification.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pulls the async afterImport callbacks out of the ImportButton JSX in
FlowTranslation and TranslateButton into handleImport functions,
matching the sibling handler pattern already used in both files.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts:
#	src/i18n/en/en.json
#	src/i18n/hi/hi.json
# Conflicts:
#	src/i18n/en/en.json
#	src/i18n/hi/hi.json
# Conflicts:
#	src/i18n/en/en.json
#	src/i18n/hi/hi.json
Ran @apollo/client-codemod-migrate-3-to-4's imports+links codemods
against .ts and .tsx separately (each needs its own --parser flag,
per the migration guide). Mechanically moves React hook imports to
@apollo/client/react, MockedProvider to @apollo/client/testing/react,
and link creator functions to their class equivalents where matched.
Followed by yarn format to normalize the codemod's double-quote
output to this repo's single-quote convention.

Does not yet compile — see follow-up commits for the remaining
manual work (addTypename removal, GraphQL result typing, auth link
rewrite).
Replaces three unmaintained third-party link packages with native v4
implementations, since none of them support v4 (apollo-link-token-refresh
imports fromPromise from @apollo/client/core, which v4 removed entirely -
confirmed as a hard build failure, not just a type mismatch):

- apollo-link-token-refresh -> a plain ApolloLink that checks
  checkAuthStatusService() and awaits renewAuthToken() before forwarding.
  Much simpler than the original's queue-based implementation because
  services/TokenRenewalService already deduplicates concurrent renewal
  calls into a single in-flight promise and already persists the
  refreshed session - this link only needs to wait for it.
- @apollo/link-context's setContext / @apollo/link-error's onError ->
  native SetContextLink / ErrorLink classes. ErrorLink's callback now
  receives a single unified  (CombinedGraphQLErrors.is(error) to
  distinguish GraphQL errors from network errors) instead of separate
  graphQLErrors/networkError properties.
- apollo-absinthe-upload-link -> a native ApolloLink using extractFiles
  (ported verbatim from that package - it's framework-agnostic, no
  Apollo dependency) and the browser's fetch() for the multipart
  request, since the original package's own fetch fallback used
  rxjs/ajax and imported now-removed @apollo/client/core internals.

Removes all four now-unused packages from package.json.
…ks, bugs)

Follow-up to the apolloclient.ts rewrite and codemod migration: closes out
the remaining tsc and test failures left by the v3->v4 jump.

- config/gql.ts wraps `gql` as TypedDocumentNode<any, any> so untyped
  documents (none of this codebase's queries use codegen) don't collapse
  useQuery/useMutation `data` to `{}` under v4's stricter default generics;
  swapped the ~66 gql imports in src/graphql to use it.
- Configured LocalState on the real Apollo client and via MockedProvider's
  defaultProps in setupTests.ts - v4 throws on any query with @client
  fields (NOTIFICATION, ERROR_MESSAGE, SEARCH_OFFSET, SCROLL_HEIGHT) once
  it misses the cache, unless LocalState is present.
- Set MockLink.defaultOptions.delay = 1 globally - v4's MockedProvider
  defaults to a randomized "realistic" network delay instead of resolving
  on the next macrotask, which broke synchronous assertions across ~30
  test files.
- Fixed a real bug this exposed: MockedResponse has no `variableMatcher`
  field (silently ignored, never real Apollo API) across 17 files - moved
  the matcher into `request.variables` as v4 actually expects.
- Migrated remaining onCompleted/onError usages (useQuery/useLazyQuery
  dropped both in v4; useMutation keeps them) and moved useLazyQuery's
  `variables` from hook options to the execute() call.

Also fixed genuine bugs surfaced along the way, not just migration noise:
- App.tsx: v4 skips the entire link chain (including refreshTokenLink) for
  queries that are 100% @client fields, so the proactive token-refresh
  check that used to piggyback on ErrorHandler's query needed to move to
  its own mount effect.
- Billing.tsx, FlowTranslation.tsx, CollectionList.tsx: `const { data,
  error } = await mutate()` assumed GraphQL errors resolve with an
  `error` field; v4's default errorPolicy rejects the promise instead, so
  these silently swallowed failures until wrapped in try/catch.
- common/notification.ts: setErrorMessage read error.networkError/
  .graphQLErrors, which no longer exist now that ApolloError is gone in
  favor of a single `error` - now checks CombinedGraphQLErrors.is(error).
- List.tsx: the row-fetch effect depended on the `filters` prop by
  reference; several callers pass a fresh object literal every render,
  which under v4's stricter fetchMore/cache-only rules turned into
  runaway refetches that exhausted test mock buffers. Now keyed on
  JSON.stringify(filters).
- ChatMessages.tsx: switched its search query from cache-only to
  cache-first, since v4 no longer allows fetchMore on a cache-only query.

Net result: tsc --noEmit clean (was 892 errors mid-migration), full
vitest suite green (1828/1828 non-skipped, stable across repeat runs).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 17, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c91db12f-c83c-4468-8ea0-1ad0b89815d1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cypress

cypress Bot commented Aug 17, 2026 •

Copy link
Copy Markdown

Glific    Run #14423

Run Properties:  status check failed Failed #14423  •  git commit 71e2c6ff83 ℹ️: Merge 29ed0a88c35571e76f44ecb02af02b46bc884732 into df8dda6e093b6e5267d8a5da89b4...
Project Glific
Branch Review apollo-client-v4-upgrade
Run status status check failed Failed #14423
Run duration 09m 04s
Commit git commit 71e2c6ff83 ℹ️: Merge 29ed0a88c35571e76f44ecb02af02b46bc884732 into df8dda6e093b6e5267d8a5da89b4...
Committer Akansha Sakhre
View all properties for this run ↗︎

Test results
Tests that failed  Failures 2
Tests that were flaky  Flaky 1
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 37
View all changes introduced in this branch ↗︎

Tests for review

Failed  interactiveMessage/InteractiveMessage.spec.ts • 1 failed test

View Output

Test Artifacts
Interactive message quick reply > should edit quick reply Test Replay Screenshots
Failed  roles/staff/collection/Collection.spec.ts • 1 failed test

View Output

Test Artifacts
Role - Staff - Collection > should remove member from collection Test Replay Screenshots
Flakiness  cypress/e2e/roles/staff/collection/Collection.spec.ts • 1 flaky test

View Output

Test Artifacts
Role - Staff - Collection > should add member to collection Test Replay Screenshots

@github-actions

Copy link
Copy Markdown

@github-actions
github-actions Bot temporarily deployed to pull request August 18, 2026 02:11 Inactive
The advanced-search date fields render asynchronously (behind a query
that resolves instantly on a fast local machine but not necessarily on a
slower/contended CI runner), and these tests read them with a single
non-retrying queryByTestId right after the dialog's title appears. On CI
this raced: the field lookup returned null, the conditional fill was
silently skipped, and the resulting empty date field failed the submit
without ever calling navigate - all 3 tests failed consistently in CI
while passing locally every time.

Switched to findByTestId (retries until the field exists) and bumped the
final navigate-assertion waitFor to 5000ms to absorb the remaining
multi-hop async chain (filter submit -> refetch -> render -> navigate)
under CI's slower scheduling.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pull request August 18, 2026 02:35 Inactive
…ients

clientForContact/clientForCollection/clientForGroup construct their own
ApolloClient directly (not via MockedProvider), so the MockedProvider
defaultProps.localState fix in setupTests.ts doesn't reach them. Apollo
Client 4 throws once a query with @client fields (SCROLL_HEIGHT, in
ConversationList) misses the cache without LocalState configured -
didn't fail any assertion here (the exception is thrown async, after the
relevant test already completed), but it printed an uncaught Invariant
Violation on every run. Same fix already applied to GroupInterface.test.tsx.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pull request August 18, 2026 02:52 Inactive
A bare vi.fn() returns undefined, and Apollo Client 4's HttpLink calls
.then() on the fetch call directly - any query that isn't fully mocked
and genuinely reaches this (e.g. ConversationList.test.tsx's raw
ApolloClient + a real fetchMore() call with no matching mock) threw a
synchronous TypeError buried in rxjs/Apollo internals instead of a normal
catchable async rejection. Returning a rejected promise instead gives a
clear "fetch is not mocked in tests" error when this happens.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pull request August 18, 2026 02:57 Inactive
The date-range filter used dayjs(...).utc() before formatting, which
shifts the calendar date backward for any timezone ahead of UTC (e.g.
IST) - so the query variables sent to the backend never matched what
the CI runner (UTC) actually computed. Drop the erroneous .utc() call
and correct the mocks to the un-shifted dates.

Also add missing .catch() handlers to several imperative Apollo
promise chains in ChatMessages/ConversationList/AskGlific, and filter
Apollo Client 4's benign "AbortError"/"QueryManager stopped" rejection
that fires whenever an ObservableQuery is torn down before test
cleanup - both were surfacing as unhandled-rejection test failures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pull request August 18, 2026 04:34 Inactive
@codecov

codecov Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.71403% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.68%. Comparing base (df8dda6) to head (29ed0a8).
⚠️ Report is 38 commits behind head on master.

Files with missing lines Patch % Lines
...onversations/ConversationList/ConversationList.tsx 68.42% 3 Missing and 3 partials ⚠️
src/containers/Chat/ChatMessages/ChatMessages.tsx 80.00% 1 Missing and 4 partials ⚠️
src/config/apolloclient.ts 92.00% 1 Missing and 3 partials ⚠️
...ractiveMessage/TranslateButton/TranslateButton.tsx 87.87% 0 Missing and 4 partials ⚠️
src/containers/SettingList/Billing/Billing.tsx 88.88% 0 Missing and 4 partials ⚠️
...c/containers/WhatsAppForms/Configure/Configure.tsx 91.66% 0 Missing and 3 partials ⚠️
src/common/notification.ts 33.33% 0 Missing and 2 partials ⚠️
src/components/floweditor/FlowEditor.tsx 94.28% 0 Missing and 2 partials ⚠️
...sistants/AssistantOptions/KnowledgeBaseOptions.tsx 75.00% 0 Missing and 2 partials ⚠️
...c/containers/NotificationList/NotificationList.tsx 88.23% 0 Missing and 2 partials ⚠️
... and 6 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4144      +/-   ##
==========================================
+ Coverage   82.91%   83.68%   +0.77%     
==========================================
  Files         368      370       +2     
  Lines       16171    16317     +146     
  Branches     3865     3900      +35     
==========================================
+ Hits        13408    13655     +247     
+ Misses       1657     1555     -102     
- Partials     1106     1107       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@akanshaaa19
akanshaaa19 marked this pull request as draft August 18, 2026 04:54
@akanshaaa19 akanshaaa19 linked an issue Aug 18, 2026 that may be closed by this pull request
Codecov's patch-coverage gate required 82.91% but the migration diff
sat at 78.42%, with 118 changed lines across ~20 files untested -
mostly the rewritten apolloclient.ts link chain, extractFiles.ts (the
new multipart-upload helper), and error/edge-case branches in several
containers that changed shape under Apollo v4's stricter mutation
error handling.

Adds src/config/apolloclient.test.ts and src/config/extractFiles.test.ts
(neither had coverage before), plus targeted error-path tests and mock
factories across ConversationList, ChatMessages, Configure, Billing,
and eight smaller containers. One test-hygiene fix along the way:
Billing.test.tsx had a test that returned before awaiting its success
UI, leaving a shared mock's in-flight call to race the next test.

A handful of lines (ConversationList.tsx:286-287,455 and
ChatMessages.tsx:716) remain uncovered - traced and confirmed as
genuinely unreachable dead code rather than missing tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pull request August 18, 2026 05:37 Inactive
retryIf treated any error without a statusCode of 500/400/401 as
retryable, which includes CombinedGraphQLErrors - a normal, successful
HTTP response carrying a GraphQL-level error like "name already
exists" or "contact is blocked". Retrying a deterministic error just
repeats the same failure up to 5 times with exponential backoff
(~9s worst case) before it ever reaches the UI.

This was masked in the unit suite (no wall-clock assertion budget) but
surfaced in CI's Cypress run as widespread "element/toast never
appeared within 8000ms" failures across several unrelated specs
(Flow, FlowEditor, StaffManagement, Search, Collection) - the
retries were eating into Cypress's default assertion timeout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions
github-actions Bot temporarily deployed to pull request August 18, 2026 06:04 Inactive

This branch was previously deployed

1 inactive deployment
pull request — 29ed0a88 Deployed Aug 18, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade Apollo Client to v4

1 participant