@@ -17,6 +17,8 @@ import { stripSlashes } from '../../src/utils'
1717import memory from 'feathers-memory'
1818import { makeTodos } from '../fixtures/todos'
1919import Vuex from 'vuex'
20+ import { performance } from 'perf_hooks'
21+ import enableServiceEvents from '../../src/service-module/service-module.events'
2022
2123interface Options {
2224 idField : string
@@ -200,6 +202,14 @@ function makeSocketIoContext() {
200202 }
201203 }
202204
205+ class TodoDebounced extends BaseModel {
206+ public static modelName = 'TodoDebounced'
207+ public static test = true
208+ public constructor ( data = { } , options ?) {
209+ super ( data , options )
210+ }
211+ }
212+
203213 const store = new Vuex . Store < RootState > ( {
204214 strict : true ,
205215 plugins : [
@@ -209,22 +219,38 @@ function makeSocketIoContext() {
209219 servicePath : 'things'
210220 } ) ,
211221 makeServicePlugin ( {
212- Model : Thing ,
222+ Model : ThingDebounced ,
213223 service : feathersSocketioClient . service ( 'things-debounced' ) ,
214224 servicePath : 'things-debounced' ,
215225 debounceEventsTime : 20 ,
216226 namespace : 'things-debounced'
227+ } ) ,
228+ makeServicePlugin ( {
229+ Model : TodoDebounced ,
230+ service : feathersSocketioClient . service ( 'todos-debounced' ) ,
231+ servicePath : 'todos-debounced' ,
232+ debounceEventsTime : 20 ,
233+ namespace : 'todos-debounced'
217234 } )
218235 ]
219236 } )
220237
238+ const debouncedQueue = enableServiceEvents ( {
239+ Model : TodoDebounced ,
240+ service : feathersSocketioClient . service ( 'todos-debounced' ) ,
241+ store,
242+ options : store . state [ 'todos-debounced' ]
243+ } )
244+
221245 return {
222246 feathersSocketioClient,
223247 makeServicePlugin,
224248 BaseModel,
225249 Thing,
226250 ThingDebounced,
227- store
251+ TodoDebounced,
252+ store,
253+ debouncedQueue
228254 }
229255}
230256
@@ -700,6 +726,7 @@ describe('Service Module', function () {
700726 isRemovePending : false ,
701727 keepCopiesInStore : false ,
702728 debounceEventsTime : null ,
729+ debounceEventsMaxWait : 1000 ,
703730 keyedById : { } ,
704731 nameStyle : 'short' ,
705732 namespace : 'service-todos' ,
@@ -1022,6 +1049,8 @@ describe('Service Module', function () {
10221049 BaseModel,
10231050 Thing,
10241051 ThingDebounced,
1052+ TodoDebounced,
1053+ debouncedQueue,
10251054 store
10261055 } = makeSocketIoContext ( )
10271056
@@ -1044,6 +1073,8 @@ describe('Service Module', function () {
10441073 } )
10451074
10461075 it ( 'created debounced' , function ( done ) {
1076+ const { debounceEventsTime } = store . state [ 'things-debounced' ]
1077+
10471078 // eslint-disable-next-line @typescript-eslint/no-unused-vars
10481079 const listener = item => {
10491080 assert (
@@ -1059,7 +1090,7 @@ describe('Service Module', function () {
10591090 . service ( 'things-debounced' )
10601091 . off ( 'created' , listener )
10611092 done ( )
1062- } , 30 )
1093+ } , debounceEventsTime * 2 )
10631094 }
10641095 // eslint-disable-next-line @typescript-eslint/no-unused-vars
10651096 feathersSocketioClient . service ( 'things-debounced' ) . on ( 'created' , listener )
@@ -1083,6 +1114,9 @@ describe('Service Module', function () {
10831114 } )
10841115
10851116 it ( 'patched debounced' , function ( done ) {
1117+ const { debounceEventsTime } = store . state [ 'things-debounced' ]
1118+
1119+ store . commit ( 'things-debounced/clearAll' )
10861120 store . commit ( 'things-debounced/addItem' , { id : 1 , test : false } )
10871121
10881122 // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1096,7 +1130,7 @@ describe('Service Module', function () {
10961130 store . state [ 'things-debounced' ] . keyedById [ 1 ] . test ,
10971131 'the item received from the socket event was updated in the store'
10981132 )
1099- } , 30 )
1133+ } , debounceEventsTime * 2 )
11001134 feathersSocketioClient
11011135 . service ( 'things-debounced' )
11021136 . off ( 'patched' , listener )
@@ -1126,6 +1160,9 @@ describe('Service Module', function () {
11261160 } )
11271161
11281162 it ( 'updated debounced' , function ( done ) {
1163+ const { debounceEventsTime } = store . state [ 'things-debounced' ]
1164+
1165+ store . commit ( 'things-debounced/clearAll' )
11291166 store . commit ( 'things-debounced/addItem' , { id : 1 , test : false } )
11301167
11311168 // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1140,7 +1177,7 @@ describe('Service Module', function () {
11401177 'the item received from the socket event was updated in the store'
11411178 )
11421179 done ( )
1143- } , 30 )
1180+ } , debounceEventsTime * 2 )
11441181 feathersSocketioClient
11451182 . service ( 'things-debounced' )
11461183 . off ( 'updated' , listener )
@@ -1170,6 +1207,9 @@ describe('Service Module', function () {
11701207 } )
11711208
11721209 it ( 'removed debounced' , function ( done ) {
1210+ const { debounceEventsTime } = store . state [ 'things-debounced' ]
1211+
1212+ store . commit ( 'things-debounced/clearAll' )
11731213 store . commit ( 'things-debounced/addItem' , { id : 1 , test : false } )
11741214
11751215 // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1185,7 +1225,7 @@ describe('Service Module', function () {
11851225 'the item received from the socket event was removed from the store'
11861226 )
11871227 done ( )
1188- } , 30 )
1228+ } , debounceEventsTime * 2 )
11891229
11901230 feathersSocketioClient
11911231 . service ( 'things-debounced' )
@@ -1196,5 +1236,137 @@ describe('Service Module', function () {
11961236
11971237 feathersSocketioClient . service ( 'things-debounced' ) . remove ( 1 )
11981238 } )
1239+
1240+ it ( 'debounce works with plenty items' , function ( done ) {
1241+ store . commit ( 'things-debounced/clearAll' )
1242+
1243+ const { debounceEventsTime } = store . state [ 'things-debounced' ]
1244+
1245+ const itemsCount = 100
1246+ let i = 0
1247+
1248+ assert (
1249+ Object . keys ( store . state [ 'things-debounced' ] . keyedById ) . length === 0 ,
1250+ 'no items at start'
1251+ )
1252+
1253+ const setTimeoutCreate = ( ) => {
1254+ setTimeout ( ( ) => {
1255+ feathersSocketioClient
1256+ . service ( 'things-debounced' )
1257+ . create ( { test : true , i } )
1258+ i ++
1259+ assert (
1260+ Object . keys ( store . state [ 'things-debounced' ] . keyedById ) . length === 0 ,
1261+ `no items at i: ${ i } `
1262+ )
1263+ if ( i < itemsCount ) {
1264+ setTimeoutCreate ( )
1265+ } else {
1266+ setTimeout ( ( ) => {
1267+ assert (
1268+ Object . keys (
1269+ store . state [ 'things-debounced' ] . keyedById . length ===
1270+ itemsCount
1271+ ) ,
1272+ 'all items are in store'
1273+ )
1274+ done ( )
1275+ } , debounceEventsTime * 2 )
1276+ }
1277+ } , debounceEventsTime / 4 )
1278+ }
1279+ setTimeoutCreate ( )
1280+ } )
1281+
1282+ it ( 'debounced events get invoked during continuous events' , function ( done ) {
1283+ store . commit ( 'things-debounced/clearAll' )
1284+
1285+ const { debounceEventsTime, debounceEventsMaxWait } = store . state [
1286+ 'things-debounced'
1287+ ]
1288+
1289+ assert (
1290+ Object . keys ( store . state [ 'things-debounced' ] . keyedById ) . length === 0 ,
1291+ 'no items at start'
1292+ )
1293+ assert ( debounceEventsMaxWait > 0 , 'maxWait is set' )
1294+
1295+ const startedAt = performance . now ( )
1296+ let i = 0
1297+
1298+ const setTimeoutCreate = ( ) => {
1299+ setTimeout ( ( ) => {
1300+ feathersSocketioClient
1301+ . service ( 'things-debounced' )
1302+ . create ( { test : true , i } )
1303+ i ++
1304+ const timePassed = Math . floor (
1305+ performance . now ( ) - startedAt - debounceEventsTime
1306+ )
1307+ if ( timePassed <= debounceEventsMaxWait ) {
1308+ if ( performance . now ( ) - startedAt <= debounceEventsMaxWait ) {
1309+ assert (
1310+ Object . keys ( store . state [ 'things-debounced' ] . keyedById )
1311+ . length === 0 ,
1312+ `no items at i: ${ i } , milliseconds passed: ${ timePassed } `
1313+ )
1314+ }
1315+ setTimeoutCreate ( )
1316+ } else {
1317+ assert (
1318+ Object . keys ( store . state [ 'things-debounced' ] . keyedById ) . length ===
1319+ i - 1 ,
1320+ `items are inserted after maxWait`
1321+ )
1322+ done ( )
1323+ }
1324+ } , debounceEventsTime / 4 )
1325+ }
1326+ setTimeoutCreate ( )
1327+ } )
1328+
1329+ it ( 'debounded remove after addOrUpdate also removes addOrUpdate queue and vise versa' , function ( ) {
1330+ const { idField } = store . state [ 'todos-debounced' ]
1331+
1332+ assert (
1333+ Object . keys ( debouncedQueue . addOrUpdateById ) . length === 0 ,
1334+ "'addOrUpdateById' initially empty"
1335+ )
1336+
1337+ assert (
1338+ Object . keys ( debouncedQueue . removeItemById ) . length === 0 ,
1339+ "'removeItemById' initially empty"
1340+ )
1341+
1342+ debouncedQueue . enqueueAddOrUpdate ( { [ idField ] : 1 , test : true } )
1343+
1344+ assert (
1345+ debouncedQueue . addOrUpdateById [ 1 ] ,
1346+ "queued item for 'addOrUpdate' correctly"
1347+ )
1348+
1349+ debouncedQueue . enqueueRemoval ( { [ idField ] : 1 , test : false } )
1350+
1351+ assert (
1352+ ! debouncedQueue . addOrUpdateById [ 1 ] ,
1353+ "queued item for 'addOrUpdate' removed immediately"
1354+ )
1355+ assert (
1356+ debouncedQueue . removeItemById [ 1 ] ,
1357+ 'queued item for removal correctly'
1358+ )
1359+
1360+ debouncedQueue . enqueueAddOrUpdate ( { [ idField ] : 1 , test : true } )
1361+
1362+ assert (
1363+ debouncedQueue . addOrUpdateById [ 1 ] ,
1364+ "queued item for 'addOrUpdate' correctly again"
1365+ )
1366+ assert (
1367+ ! debouncedQueue . removeItemById [ 1 ] ,
1368+ "queued item for 'remove' removed immediately"
1369+ )
1370+ } )
11991371 } )
12001372} )
0 commit comments