• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Freegle / Iznik / 11495

09 May 2026 07:35AM UTC coverage: 69.06% (-3.8%) from 72.847%
11495

Pull #408

circleci

edwh
docs(migration): mark restartproject and repaircafewales as migrated (PR #408)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pull Request #408: feat(batch): migrate check_cgas, visualise, tn_sync + dry-run improvements

9127 of 10554 branches covered (86.48%)

Branch coverage included in aggregate %.

507 of 663 new or added lines in 16 files covered. (76.47%)

11902 existing lines in 138 files now uncovered.

101630 of 149824 relevant lines covered (67.83%)

19.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.94
/iznik-nuxt3/components/InfiniteLoading.vue
1
<template>
1✔
2
  <client-only>
1✔
3
    <div
1✔
4
      :key="bump"
1✔
5
      v-observe-visibility="{
1✔
6
        callback: visibilityChanged,
7
        options: observerOptions,
8
      }"
9
      class="infinite-loader"
1✔
10
    >
11
      <slot v-if="state == 'loading'" name="spinner"></slot>
1✔
12
      <slot v-if="state == 'complete'" name="complete"></slot>
1✔
13
      <slot v-if="state == 'error'" name="error"></slot>
1✔
14
    </div>
1✔
15
  </client-only>
1✔
16
</template>
17
<script setup>
18
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
1✔
19

20
// Derived from https://github.com/oumoussa98/vue3-infinite-loading.  Reworked radically to allow an async event
1✔
21
// handler, and to make consistent with the rest of this codebase.
1✔
22
const props = defineProps({
1✔
23
  top: { type: Boolean, required: false },
24
  target: { type: [String, Boolean], required: false, default: null },
25
  distance: { type: Number, required: false, default: 0 },
26
  identifier: { type: [String, Number], required: false, default: null },
27
  firstload: { type: Boolean, required: false, default: true },
28
  slots: { type: Object, required: false, default: null },
29
  forceUseInfiniteWrapper: { type: String, required: false, default: null },
30
})
31

32
// When a scroll wrapper is specified, use it as the IntersectionObserver root.
1✔
33
// This is needed when the infinite loader is inside a nested scroll container
1✔
34
// (e.g. a chat list with overflow-y: auto) — without this, the observer uses
1✔
35
// the document viewport and never detects scrolling within the container.
1✔
36
const observerOptions = computed(() => {
1✔
37
  const opts = {
35✔
38
    rootMargin: '0px 0px ' + props.distance + 'px 0px',
35✔
39
  }
35✔
40

41
  if (props.forceUseInfiniteWrapper) {
35!
UNCOV
42
    const el = document.querySelector(props.forceUseInfiniteWrapper)
×
UNCOV
43
    if (el) {
×
UNCOV
44
      opts.root = el
×
UNCOV
45
    }
×
UNCOV
46
  }
×
47

48
  return opts
35✔
49
})
35✔
50

51
const emit = defineEmits(['infinite'])
1✔
52

53
const state = ref('ready')
1✔
54
const bump = ref(0)
1✔
55
const visible = ref(false)
1✔
56
let timer = null
1✔
57

58
// Methods
1✔
59
function visibilityChanged(isVisible) {
3✔
60
  visible.value = isVisible
3✔
61
}
3✔
62

63
function loading() {
8✔
64
  state.value = 'loading'
8✔
65
}
8✔
66

67
function loaded() {
1✔
68
  state.value = 'loaded'
1✔
69
}
1✔
70

71
function complete() {
6✔
72
  state.value = 'complete'
6✔
73
}
6✔
74

75
function error() {
2✔
76
  state.value = 'error'
2✔
77
}
2✔
78

79
function stopObserver() {
4✔
80
  complete()
4✔
81
}
4✔
82

83
async function emitInfinite() {
6✔
84
  loading()
6✔
85

86
  // Wait for the next tick otherwise if the event handlers return synchronously we may not end up triggering
6✔
87
  // the watch.
6✔
88
  await nextTick()
6✔
89
  emit('infinite', {
6✔
90
    loading,
6✔
91
    loaded,
6✔
92
    complete,
6✔
93
    error,
6✔
94
    stopObserver,
6✔
95
  })
6✔
96
}
6✔
97

UNCOV
98
function fallback() {
×
UNCOV
99
  timer = null
×
100

UNCOV
101
  if (state.value === 'complete') {
×
UNCOV
102
    return
×
UNCOV
103
  }
×
104

UNCOV
105
  if (visible.value && state.value === 'loaded') {
×
UNCOV
106
    // We have loaded and not completed, and yet it's still visible.  We need to do some more.
×
UNCOV
107
    emitInfinite()
×
UNCOV
108
  }
×
109

UNCOV
110
  timer = setTimeout(fallback, 100)
×
UNCOV
111
}
×
112

113
// Watch state changes
1✔
114
watch(state, (newVal) => {
1✔
115
  // console.log('state changed', newVal)
14✔
116
  if (newVal === 'loading') {
14✔
117
    // This is an internal change - nothing to do.
8✔
118
  } else if (newVal === 'complete') {
14✔
119
    // console.log('Complete, stop observer')
3✔
120
    stopObserver()
3✔
121
  } else {
3✔
122
    const parentEl = props.target || document.documentElement
3✔
123
    const prevHeight = parentEl.scrollHeight
3✔
124

125
    if (newVal === 'loaded' && props.top) {
3!
UNCOV
126
      // console.log('Adjust scrollTop')
×
UNCOV
127
      parentEl.scrollTop = parentEl.scrollHeight - prevHeight
×
UNCOV
128
    }
×
129
  }
3✔
130
})
14✔
131

132
// Watch identifier changes
1✔
133
watch(
1✔
134
  () => props.identifier,
1✔
135
  () => {
1✔
136
    // We've been asked to kick the component to reset it.
2✔
137
    bump.value++
2✔
138
    emitInfinite()
2✔
139
    // Restart the fallback loop if it stopped because state was 'complete'.
2✔
140
    if (!timer) {
2!
UNCOV
141
      timer = setTimeout(fallback, 100)
×
UNCOV
142
    }
×
143
  }
2✔
144
)
1✔
145

146
// Lifecycle hooks
1✔
147
onMounted(async () => {
1✔
148
  if (props.firstload) {
35✔
149
    await emitInfinite()
1✔
150
  }
1✔
151

152
  // It would be nice if we didn't need a timer and could be purely event-driven.  But there is no guarantee
35✔
153
  // that what happens in response to our emit will result in all the components being rendered, and although
35✔
154
  // Vue3 has Suspense I can't see an easy way of waiting for all renders to finish.
35✔
155
  timer = setTimeout(fallback, 100)
35✔
156
})
35✔
157

158
onBeforeUnmount(() => {
1✔
UNCOV
159
  if (timer) {
×
UNCOV
160
    clearTimeout(timer)
×
UNCOV
161
  }
×
UNCOV
162
})
×
163

164
// Expose methods to parent components
1✔
165
defineExpose({
1✔
166
  loading,
1✔
167
  loaded,
1✔
168
  complete,
1✔
169
  error,
1✔
170
  stopObserver,
1✔
171
})
1✔
172
</script>
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc