From 6fa63dc5094fbe4bf7b9e97125cad88f326dbc44 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Mon, 20 Nov 2023 09:18:24 -0300 Subject: [PATCH] Replace the existing plugins mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A summary of the discussion on #1492: - the modular API should be the only way to pass optional functionality to the SDK - this means we need to replace the existing ClientOptions.plugins mechanism, which is currently used to pass a Vcdiff decoder - since the modular variant of the SDK only exists for web at the moment, we will bundle Vcdiff decoding into all other platforms (in which bundle size is not much of a concern) - on web, if you want deltas, you have to use the modular variant of the SDK So, we remove the ClientOptions.plugins mechanism and introduce a tree-shakable Vcdiff module, which bundles the vcdiff-decoder library (meaning that users no longer need to directly import this library). Note that this means that, currently, it is no longer possible to use deltas inside a Web Worker. We’ll address this in #1514. The README example of configuring a channel to use deltas is copied from the README of the vcdiff-decoder library. (Once ably-js v2 is released, we should update the instructions in the vcdiff-decoder library’s README to make it clear they only apply to v1. I’ve raised #1513 for this.) Resolves #1492. --- README.md | 70 +++- ably.d.ts | 10 - modules.d.ts | 19 + scripts/moduleReport.ts | 1 + src/common/lib/client/baserealtime.ts | 5 +- src/common/lib/client/modulesmap.ts | 2 + src/common/lib/client/realtimechannel.ts | 2 +- src/common/lib/types/message.ts | 19 +- src/common/platform.ts | 7 + src/platform/nativescript/index.ts | 2 + src/platform/nodejs/index.ts | 2 + src/platform/react-native/index.ts | 2 + src/platform/web/index.ts | 5 + src/platform/web/modules.ts | 2 + src/platform/web/modules/vcdiff.ts | 1 + test/browser/modules.test.js | 36 +- test/common/globals/named_dependencies.js | 4 - test/realtime/delta.test.js | 28 +- test/realtime/shared/delta_tests.js | 418 +++++++++++----------- test/support/browser_file_list.js | 1 - test/support/browser_setup.js | 3 - 21 files changed, 391 insertions(+), 248 deletions(-) create mode 100644 src/platform/web/modules/vcdiff.ts diff --git a/README.md b/README.md index 55f1ab07e5..d32388a23d 100644 --- a/README.md +++ b/README.md @@ -200,12 +200,73 @@ channel.subscribe('myEvent', function (message) { Subscribing to a channel in delta mode enables [delta compression](https://www.ably.com/docs/realtime/channels/channel-parameters/deltas). This is a way for a client to subscribe to a channel so that message payloads sent contain only the difference (ie the delta) between the present message and the previous message on the channel. -Configuring a channel for deltas is detailed in the [@ably-forks/vcdiff-decoder documentation](https://github.com/ably-forks/vcdiff-decoder#usage). +To subscribe to a channel in delta mode, you must: + +1. Create a client that supports deltas (this only applies when running in a browser); +2. Configure the channel to operate in delta mode. + +#### Creating a client that supports deltas + +This section only applies when running in a browser. The Realtime client on all other platforms includes delta support. + +To use delta functionality in the browser, you must use the [modular variant of the library](#modular-tree-shakable-variant) and create a client that includes the `Vcdiff` module: + + ```javascript + import { BaseRealtime, WebSocketTransport, FetchRequest, Vcdiff } from 'ably/modules'; + + const options = { key: 'YOUR_ABLY_KEY' }; + const client = new BaseRealtime(options, { + WebSocketTransport, + FetchRequest, + Vcdiff + }); + ``` + +#### Configuring a channel to operate in delta mode + +To configure a channel to operate in delta mode, specify channel parameters of `{ delta: 'vcdiff' }` when fetching the channel: + +```javascript +const channel = realtime.channels.get('your-ably-channel', { + params: { + delta: 'vcdiff' + } +}); +``` Beyond specifying channel options, the rest is transparent and requires no further changes to your application. The `message.data` instances that are delivered to your listening function continue to contain the values that were originally published. If you would like to inspect the `Message` instances in order to identify whether the `data` they present was rendered from a delta message from Ably then you can see if `extras.delta.format` equals `'vcdiff'`. +## Delta Plugin + +From version 1.2 this client library supports subscription to a stream of Vcdiff formatted delta messages from the Ably service. For certain applications this can bring significant data efficiency savings. +This is an optional feature so our + +See the [@ably-forks/vcdiff-decoder documentation](https://github.com/ably-forks/vcdiff-decoder#usage) for setup and usage examples. + + +```javascript +const Ably = require('ably'); +const vcdiffPlugin = require('@ably/vcdiff-decoder'); + +const realtime = new Ably.Realtime({ + key: 'YOUR_ABLY_KEY', + plugins: { + vcdiff: vcdiffPlugin + }, + log: { level: 4 } // optional +}); + +const channel = realtime.channels.get('your-ably-channel', { + params: { + delta: 'vcdiff' + } +}); + +channel.subscribe(msg => console.log("Received message: ", msg)); +``` + ### Publishing to a channel ```javascript @@ -466,13 +527,6 @@ const nextPage = await statsPage.next(); // retrieves the next page as Pa const time = await client.time(); // time is in ms since epoch ``` -## Delta Plugin - -From version 1.2 this client library supports subscription to a stream of Vcdiff formatted delta messages from the Ably service. For certain applications this can bring significant data efficiency savings. -This is an optional feature so our - -See the [@ably-forks/vcdiff-decoder documentation](https://github.com/ably-forks/vcdiff-decoder#usage) for setup and usage examples. - ## Support, feedback and troubleshooting Please visit http://support.ably.com/ for access to our knowledgebase and to ask for any assistance. diff --git a/ably.d.ts b/ably.d.ts index ffaf96a8ee..bd7a7a5dac 100644 --- a/ably.d.ts +++ b/ably.d.ts @@ -565,16 +565,6 @@ declare namespace Types { * @defaultValue 10s */ realtimeRequestTimeout?: number; - - /** - * A map between a plugin type and a plugin object. - */ - plugins?: { - /** - * A plugin capable of decoding `vcdiff`-encoded messages. For more information on how to configure a channel to use delta encoding, see the [documentation for the `@ably-forks/vcdiff-decoder` package](https://github.com/ably-forks/vcdiff-decoder#usage). - */ - vcdiff?: any; - }; } /** diff --git a/modules.d.ts b/modules.d.ts index 0ea246a164..47006020f2 100644 --- a/modules.d.ts +++ b/modules.d.ts @@ -162,6 +162,20 @@ export declare const FetchRequest: unknown; */ export declare const MessageInteractions: unknown; +/** + * Provides a {@link BaseRealtime} instance with the ability to use [delta compression](https://www.ably.com/docs/realtime/channels/channel-parameters/deltas). + * + * To create a client that includes this module, include it in the `ModulesMap` that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest, Vcdiff } from 'ably/modules'; + * const realtime = new BaseRealtime(options, { WebSocketTransport, FetchRequest, Vcdiff }); + * ``` + * + * For information on how to configure a channel to use delta encoding, see [the documentation in the `README`](https://github.com/ably/ably-js/blob/main/README.md#configuring-a-channel-to-operate-in-delta-mode). + */ +export declare const Vcdiff: unknown; + /** * Pass a `ModulesMap` to { @link BaseRest.constructor | the constructor of BaseRest } or {@link BaseRealtime.constructor | that of BaseRealtime} to specify which functionality should be made available to that client. */ @@ -215,6 +229,11 @@ export interface ModulesMap { * See {@link MessageInteractions | documentation for the `MessageInteractions` module}. */ MessageInteractions?: typeof MessageInteractions; + + /** + * See {@link Vcdiff | documentation for the `Vcdiff` module}. + */ + Vcdiff?: typeof Vcdiff; } /** diff --git a/scripts/moduleReport.ts b/scripts/moduleReport.ts index f3deda7b30..56ebce35c2 100644 --- a/scripts/moduleReport.ts +++ b/scripts/moduleReport.ts @@ -17,6 +17,7 @@ const moduleNames = [ 'XHRRequest', 'FetchRequest', 'MessageInteractions', + 'Vcdiff', ]; // List of all free-standing functions exported by the library along with the diff --git a/src/common/lib/client/baserealtime.ts b/src/common/lib/client/baserealtime.ts index 3787709326..821b86b6e5 100644 --- a/src/common/lib/client/baserealtime.ts +++ b/src/common/lib/client/baserealtime.ts @@ -11,13 +11,15 @@ import ClientOptions from '../../types/ClientOptions'; import * as API from '../../../../ably'; import { ModulesMap, RealtimePresenceModule } from './modulesmap'; import { TransportNames } from 'common/constants/TransportName'; -import { TransportImplementations } from 'common/platform'; +import Platform, { TransportImplementations } from 'common/platform'; +import { VcdiffDecoder } from '../types/message'; /** `BaseRealtime` is an export of the tree-shakable version of the SDK, and acts as the base class for the `DefaultRealtime` class exported by the non tree-shakable version. */ class BaseRealtime extends BaseClient { readonly _RealtimePresence: RealtimePresenceModule | null; + readonly _decodeVcdiff: VcdiffDecoder | null; // Extra transport implementations available to this client, in addition to those in Platform.Transports.bundledImplementations readonly _additionalTransportImplementations: TransportImplementations; _channels: any; @@ -28,6 +30,7 @@ class BaseRealtime extends BaseClient { Logger.logAction(Logger.LOG_MINOR, 'Realtime()', ''); this._additionalTransportImplementations = BaseRealtime.transportImplementationsFromModules(modules); this._RealtimePresence = modules.RealtimePresence ?? null; + this._decodeVcdiff = (modules.Vcdiff ?? (Platform.Vcdiff.supported && Platform.Vcdiff.bundledDecode)) || null; this.connection = new Connection(this, this.options); this._channels = new Channels(this); if (options.autoConnect !== false) this.connect(); diff --git a/src/common/lib/client/modulesmap.ts b/src/common/lib/client/modulesmap.ts index e52730aa32..7a3fb55b5a 100644 --- a/src/common/lib/client/modulesmap.ts +++ b/src/common/lib/client/modulesmap.ts @@ -10,6 +10,7 @@ import { fromValues as presenceMessageFromValues, fromValuesArray as presenceMessagesFromValuesArray, } from '../types/presencemessage'; +import { VcdiffDecoder } from '../types/message'; export interface PresenceMessageModule { presenceMessageFromValues: typeof presenceMessageFromValues; @@ -31,6 +32,7 @@ export interface ModulesMap { XHRRequest?: typeof XHRRequest; FetchRequest?: typeof fetchRequest; MessageInteractions?: typeof FilteredSubscriptions; + Vcdiff?: VcdiffDecoder; } export const allCommonModules: ModulesMap = { Rest }; diff --git a/src/common/lib/client/realtimechannel.ts b/src/common/lib/client/realtimechannel.ts index e83c32e25f..27d409ea38 100644 --- a/src/common/lib/client/realtimechannel.ts +++ b/src/common/lib/client/realtimechannel.ts @@ -122,7 +122,7 @@ class RealtimeChannel extends EventEmitter { this._attachResume = false; this._decodingContext = { channelOptions: this.channelOptions, - plugins: client.options.plugins || {}, + decodeVcdiff: client._decodeVcdiff ?? undefined, baseEncodedPreviousPayload: undefined, }; this._lastPayload = { diff --git a/src/common/lib/types/message.ts b/src/common/lib/types/message.ts index 1f149772ed..aafba56ca9 100644 --- a/src/common/lib/types/message.ts +++ b/src/common/lib/types/message.ts @@ -22,13 +22,11 @@ export type CipherOptions = { }; }; +export type VcdiffDecoder = (delta: Uint8Array, source: Uint8Array) => Uint8Array; + export type EncodingDecodingContext = { channelOptions: ChannelOptions; - plugins: { - vcdiff?: { - decode: (delta: Uint8Array, source: Uint8Array) => Uint8Array; - }; - }; + decodeVcdiff?: VcdiffDecoder; baseEncodedPreviousPayload?: Buffer | BrowserBufferlike; }; @@ -36,7 +34,6 @@ function normaliseContext(context: CipherOptions | EncodingDecodingContext | Cha if (!context || !(context as EncodingDecodingContext).channelOptions) { return { channelOptions: context as ChannelOptions, - plugins: {}, baseEncodedPreviousPayload: undefined, }; } @@ -216,8 +213,12 @@ export async function decode( throw new Error('Unable to decrypt message; not an encrypted channel'); } case 'vcdiff': - if (!context.plugins || !context.plugins.vcdiff) { - throw new ErrorInfo('Missing Vcdiff decoder (https://github.com/ably-forks/vcdiff-decoder)', 40019, 400); + if (!context.decodeVcdiff) { + if (Platform.Vcdiff.supported) { + Utils.throwMissingModuleError('Vcdiff'); + } else { + throw new ErrorInfo(Platform.Vcdiff.errorMessage, 40019, 400); + } } if (typeof Uint8Array === 'undefined') { throw new ErrorInfo( @@ -236,7 +237,7 @@ export async function decode( const deltaBaseBuffer = Platform.BufferUtils.toBuffer(deltaBase as Buffer); data = Platform.BufferUtils.toBuffer(data); - data = Platform.BufferUtils.arrayBufferViewToBuffer(context.plugins.vcdiff.decode(data, deltaBaseBuffer)); + data = Platform.BufferUtils.arrayBufferViewToBuffer(context.decodeVcdiff(data, deltaBaseBuffer)); lastPayload = data; } catch (e) { throw new ErrorInfo('Vcdiff delta decode failed with ' + e, 40018, 400); diff --git a/src/common/platform.ts b/src/common/platform.ts index 6d5a6245bf..bfd1c5fd64 100644 --- a/src/common/platform.ts +++ b/src/common/platform.ts @@ -8,6 +8,7 @@ import * as WebBufferUtils from '../platform/web/lib/util/bufferutils'; import * as NodeBufferUtils from '../platform/nodejs/lib/util/bufferutils'; import { IUntypedCryptoStatic } from '../common/types/ICryptoStatic'; import TransportName from './constants/TransportName'; +import { VcdiffDecoder } from './lib/types/message'; type Bufferlike = WebBufferUtils.Bufferlike | NodeBufferUtils.Bufferlike; type BufferUtilsOutput = WebBufferUtils.Output | NodeBufferUtils.Output; @@ -39,4 +40,10 @@ export default class Platform { }; static Defaults: IDefaults; static WebStorage: IWebStorage | null; + static Vcdiff: + | { supported: false; errorMessage: string /* explains why this platform does not support vcdiff */ } + | { + supported: true; + bundledDecode: VcdiffDecoder | null /* { supported: true, bundledDecode: null } means that the decode implementation can be provided via ModulesMap */; + }; } diff --git a/src/platform/nativescript/index.ts b/src/platform/nativescript/index.ts index 5a57dbe073..a88a8fba64 100644 --- a/src/platform/nativescript/index.ts +++ b/src/platform/nativescript/index.ts @@ -4,6 +4,7 @@ import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; +import { decode as decodeVcdiff } from '@ably/vcdiff-decoder'; // Platform Specific import BufferUtils from '../web/lib/util/bufferutils'; @@ -30,6 +31,7 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = WebStorage; +Platform.Vcdiff = { supported: true, bundledDecode: decodeVcdiff }; for (const clientClass of [DefaultRest, DefaultRealtime]) { clientClass.Crypto = Crypto; diff --git a/src/platform/nodejs/index.ts b/src/platform/nodejs/index.ts index d53b83dadd..34ffd94957 100644 --- a/src/platform/nodejs/index.ts +++ b/src/platform/nodejs/index.ts @@ -4,6 +4,7 @@ import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; +import { decode as decodeVcdiff } from '@ably/vcdiff-decoder'; // Platform Specific import BufferUtils from './lib/util/bufferutils'; @@ -26,6 +27,7 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = null; +Platform.Vcdiff = { supported: true, bundledDecode: decodeVcdiff }; for (const clientClass of [DefaultRest, DefaultRealtime]) { clientClass.Crypto = Crypto; diff --git a/src/platform/react-native/index.ts b/src/platform/react-native/index.ts index 998c2dd19a..1724fa1efa 100644 --- a/src/platform/react-native/index.ts +++ b/src/platform/react-native/index.ts @@ -4,6 +4,7 @@ import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; +import { decode as decodeVcdiff } from '@ably/vcdiff-decoder'; // Platform Specific import BufferUtils from '../web/lib/util/bufferutils'; @@ -30,6 +31,7 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = WebStorage; +Platform.Vcdiff = { supported: true, bundledDecode: decodeVcdiff }; for (const clientClass of [DefaultRest, DefaultRealtime]) { clientClass.Crypto = Crypto; diff --git a/src/platform/web/index.ts b/src/platform/web/index.ts index 9f3b0b0a06..a598774244 100644 --- a/src/platform/web/index.ts +++ b/src/platform/web/index.ts @@ -28,6 +28,11 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = WebStorage; +// To use vcdiff on web you must use the modular variant of the library +Platform.Vcdiff = { + supported: false, + errorMessage: 'For vcdiff functionality in the browser, you must use the modular variant of ably-js', +}; for (const clientClass of [DefaultRest, DefaultRealtime]) { clientClass.Crypto = Crypto; diff --git a/src/platform/web/modules.ts b/src/platform/web/modules.ts index 0465405a90..16c8343efb 100644 --- a/src/platform/web/modules.ts +++ b/src/platform/web/modules.ts @@ -22,6 +22,7 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = ModulesTransports; Platform.WebStorage = WebStorage; +Platform.Vcdiff = { supported: true, bundledDecode: null }; Http.bundledRequestImplementations = modulesBundledRequestImplementations; @@ -49,6 +50,7 @@ export * from './modules/msgpack'; export * from './modules/realtimepresence'; export * from './modules/transports'; export * from './modules/http'; +export * from './modules/vcdiff'; export { Rest } from '../../common/lib/client/rest'; export { FilteredSubscriptions as MessageInteractions } from '../../common/lib/client/filteredsubscriptions'; export { BaseRest, BaseRealtime, ErrorInfo }; diff --git a/src/platform/web/modules/vcdiff.ts b/src/platform/web/modules/vcdiff.ts new file mode 100644 index 0000000000..002dad7ea3 --- /dev/null +++ b/src/platform/web/modules/vcdiff.ts @@ -0,0 +1 @@ +export { decode as Vcdiff } from '@ably/vcdiff-decoder'; diff --git a/test/browser/modules.test.js b/test/browser/modules.test.js index 775bc05e4e..c8bc4c004c 100644 --- a/test/browser/modules.test.js +++ b/test/browser/modules.test.js @@ -20,9 +20,10 @@ import { FetchRequest, XHRRequest, MessageInteractions, + Vcdiff, } from '../../build/modules/index.js'; -function registerAblyModulesTests(helper) { +function registerAblyModulesTests(helper, registerDeltaTests) { describe('browser/modules', function () { this.timeout(10 * 1000); const expect = chai.expect; @@ -705,14 +706,43 @@ function registerAblyModulesTests(helper) { }); }); }); + + // Tests for the Vcdiff module + // + // Note: Unlike the other tests in this file, which only test how the + // absence or presence of a module affects the client, assuming that the + // underlying functionality is tested in detail in the test suite for the + // default variant of the library, the tests for the Vcdiff module actually + // test the library’s delta encoding functionality. This is because on web, + // delta encoding functionality is only available in the modular variant of + // the library. + (() => { + const config = { + createRealtimeWithDeltaPlugin: (options) => { + return new BaseRealtime(options, { + WebSocketTransport, + FetchRequest, + Vcdiff, + }); + }, + createRealtimeWithoutDeltaPlugin: (options) => { + return new BaseRealtime(options, { + WebSocketTransport, + FetchRequest, + }); + }, + }; + + registerDeltaTests('Vcdiff', config); + })(); }); } // This function is called by browser_setup.js once `require` is available window.registerAblyModulesTests = async () => { return new Promise((resolve) => { - require(['shared_helper'], (helper) => { - registerAblyModulesTests(helper); + require(['shared_helper', 'delta_tests'], (helper, registerDeltaTests) => { + registerAblyModulesTests(helper, registerDeltaTests); resolve(); }); }); diff --git a/test/common/globals/named_dependencies.js b/test/common/globals/named_dependencies.js index a7234844ec..4d25af26be 100644 --- a/test/common/globals/named_dependencies.js +++ b/test/common/globals/named_dependencies.js @@ -3,10 +3,6 @@ define(function () { return (module.exports = { // Ably modules ably: { browser: 'build/ably', node: 'build/ably-node' }, - 'vcdiff-decoder': { - browser: 'node_modules/@ably/vcdiff-decoder/dist/vcdiff-decoder', - node: 'node_modules/@ably/vcdiff-decoder', - }, // test modules globals: { browser: 'test/common/globals/environment', node: 'test/common/globals/environment' }, diff --git a/test/realtime/delta.test.js b/test/realtime/delta.test.js index 83575f46f5..14a63beaa8 100644 --- a/test/realtime/delta.test.js +++ b/test/realtime/delta.test.js @@ -1,5 +1,29 @@ 'use strict'; -define(['delta_tests'], function (registerDeltaTests) { - registerDeltaTests('realtime/delta'); +define(['shared_helper', 'delta_tests'], function (helper, runDeltaTests) { + const Platform = helper.Ably.Realtime.Platform; + + let config; + + if (Platform.Vcdiff.supported) { + if (Platform.Vcdiff.bundledDecode) { + config = { + createRealtimeWithDeltaPlugin: (options) => { + return helper.AblyRealtime(options); + }, + }; + } else { + throw new Error( + 'vcdiff is supported but not bundled; this should only be the case for the modular variant of the library, which this test doesn’t exercise' + ); + } + } else { + config = { + createRealtimeWithoutDeltaPlugin: (options) => { + return new helper.AblyRealtime(options); + }, + }; + } + + runDeltaTests('realtime/delta', config); }); diff --git a/test/realtime/shared/delta_tests.js b/test/realtime/shared/delta_tests.js index 20ccf8183e..0de7ebde1f 100644 --- a/test/realtime/shared/delta_tests.js +++ b/test/realtime/shared/delta_tests.js @@ -1,5 +1,5 @@ -define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, vcdiffDecoder, async, chai) { - function registerDeltaTests(describeLabel) { +define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { + function registerDeltaTests(describeLabel, config) { var expect = chai.expect; var displayError = helper.displayError; var closeAndFinish = helper.closeAndFinish; @@ -17,13 +17,26 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v return JSON.stringify(a) === JSON.stringify(b); } - function getTestVcdiffDecoder() { + function getTestVcdiffDecoder(realtime) { + if (!realtime._decodeVcdiff) { + throw new Error('Expected client to expose vcdiff decoder via _decodeVcdiff property'); + } + + let numberOfCalls = 0; + + const originalDecodeVcdiff = realtime._decodeVcdiff; + const testDecodeVcdiff = function (delta, base) { + numberOfCalls++; + return originalDecodeVcdiff(delta, base); + }; + + realtime._decodeVcdiff = testDecodeVcdiff; + return { - numberOfCalls: 0, - decode: function (delta, base) { - this.numberOfCalls++; - return vcdiffDecoder.decode(delta, base); + get numberOfCalls() { + return numberOfCalls; }, + decode: testDecodeVcdiff, }; } @@ -39,244 +52,237 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); }); - it('deltaPlugin', function (done) { - var testName = 'deltaPlugin'; - try { - var testVcdiffDecoder = getTestVcdiffDecoder(); - var realtime = helper.AblyRealtime({ - plugins: { - vcdiff: testVcdiffDecoder, - }, - }); - var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - - channel.on('attaching', function (stateChange) { - done( - new Error( - 'Channel reattaching, presumably due to decode failure; reason =' + displayError(stateChange.reason) - ) - ); - }); - - channel.subscribe(function (message) { - try { - var index = Number(message.name); - expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + if (config.createRealtimeWithDeltaPlugin) { + it('deltaPlugin', function (done) { + var testName = 'deltaPlugin'; + try { + var realtime = config.createRealtimeWithDeltaPlugin(helper.ablyClientOptions()); + var testVcdiffDecoder = getTestVcdiffDecoder(realtime); + var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - if (index === testData.length - 1) { - expect(testVcdiffDecoder.numberOfCalls).to.equal( - testData.length - 1, - 'Check number of delta messages' - ); - closeAndFinish(done, realtime); - } - } catch (err) { + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); } - }); - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + channel.on('attaching', function (stateChange) { + done( + new Error( + 'Channel reattaching, presumably due to decode failure; reason =' + displayError(stateChange.reason) + ) + ); + }); + + channel.subscribe(function (message) { + try { + var index = Number(message.name); + expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + + if (index === testData.length - 1) { + expect(testVcdiffDecoder.numberOfCalls).to.equal( + testData.length - 1, + 'Check number of delta messages' + ); + closeAndFinish(done, realtime); + } + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } - it('unusedPlugin', function (done) { - var testName = 'unusedPlugin'; - try { - var testVcdiffDecoder = getTestVcdiffDecoder(); - var realtime = helper.AblyRealtime({ - plugins: { - vcdiff: testVcdiffDecoder, - }, - }); - var channel = realtime.channels.get(testName); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - channel.subscribe(function (message) { - try { - var index = Number(message.name); - expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + if (config.createRealtimeWithDeltaPlugin) { + it('unusedPlugin', function (done) { + var testName = 'unusedPlugin'; + try { + var realtime = config.createRealtimeWithDeltaPlugin(helper.ablyClientOptions()); + var testVcdiffDecoder = getTestVcdiffDecoder(realtime); + var channel = realtime.channels.get(testName); - if (index === testData.length - 1) { - expect(testVcdiffDecoder.numberOfCalls).to.equal(0, 'Check number of delta messages'); - closeAndFinish(done, realtime); - } - } catch (err) { + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); } - }); + channel.subscribe(function (message) { + try { + var index = Number(message.name); + expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + if (index === testData.length - 1) { + expect(testVcdiffDecoder.numberOfCalls).to.equal(0, 'Check number of delta messages'); + closeAndFinish(done, realtime); + } + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } + + if (config.createRealtimeWithDeltaPlugin) { + it('lastMessageNotFoundRecovery', function (done) { + var testName = 'lastMessageNotFoundRecovery'; + try { + var realtime = config.createRealtimeWithDeltaPlugin(helper.ablyClientOptions()); + var testVcdiffDecoder = getTestVcdiffDecoder(realtime); + var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - it('lastMessageNotFoundRecovery', function (done) { - var testName = 'lastMessageNotFoundRecovery'; - try { - var testVcdiffDecoder = getTestVcdiffDecoder(); - var realtime = helper.AblyRealtime({ - plugins: { - vcdiff: testVcdiffDecoder, - }, - }); - var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - channel.subscribe(function (message) { - var index = Number(message.name); - try { - expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; - } catch (err) { + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); } + channel.subscribe(function (message) { + var index = Number(message.name); + try { + expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + } catch (err) { + closeAndFinish(done, realtime, err); + } - if (index === 1) { - /* Simulate issue */ - channel._lastPayload.messageId = null; - channel.once('attaching', function (stateChange) { + if (index === 1) { + /* Simulate issue */ + channel._lastPayload.messageId = null; + channel.once('attaching', function (stateChange) { + try { + expect(stateChange.reason.code).to.equal(40018, 'Check error code passed through per RTL18c'); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + channel.on('attaching', function (stateChange) { + closeAndFinish( + done, + realtime, + new Error('Check no further decode failures; reason =' + displayError(stateChange.reason)) + ); + }); + }); + } else if (index === testData.length - 1) { try { - expect(stateChange.reason.code).to.equal(40018, 'Check error code passed through per RTL18c'); + expect(testVcdiffDecoder.numberOfCalls).to.equal( + testData.length - 2, + 'Check number of delta messages' + ); } catch (err) { closeAndFinish(done, realtime, err); return; } - channel.on('attaching', function (stateChange) { - closeAndFinish( - done, - realtime, - new Error('Check no further decode failures; reason =' + displayError(stateChange.reason)) - ); - }); - }); - } else if (index === testData.length - 1) { - try { - expect(testVcdiffDecoder.numberOfCalls).to.equal( - testData.length - 2, - 'Check number of delta messages' - ); - } catch (err) { - closeAndFinish(done, realtime, err); - return; + closeAndFinish(done, realtime); } - closeAndFinish(done, realtime); - } - }); + }); - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } - it('deltaDecodeFailureRecovery', function (done) { - var testName = 'deltaDecodeFailureRecovery'; - try { - var failingTestVcdiffDecoder = { - decode: function (delta, base) { + if (config.createRealtimeWithDeltaPlugin) { + it('deltaDecodeFailureRecovery', function (done) { + var testName = 'deltaDecodeFailureRecovery'; + try { + var realtime = config.createRealtimeWithDeltaPlugin(helper.ablyClientOptions()); + + realtime._decodeVcdiff = function (delta, base) { throw new Error('Failed to decode delta.'); - }, - }; - - var realtime = helper.AblyRealtime({ - plugins: { - vcdiff: failingTestVcdiffDecoder, - }, - }); - var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - channel.on('attaching', function (stateChange) { - try { - expect(stateChange.reason.code).to.equal(40018, 'Check error code passed through per RTL18c'); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - channel.subscribe(function (message) { - var index = Number(message.name); - try { - expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; - } catch (err) { + }; + + var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); + + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); } + channel.on('attaching', function (stateChange) { + try { + expect(stateChange.reason.code).to.equal(40018, 'Check error code passed through per RTL18c'); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + channel.subscribe(function (message) { + var index = Number(message.name); + try { + expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + } catch (err) { + closeAndFinish(done, realtime, err); + } - if (index === testData.length - 1) { - closeAndFinish(done, realtime); - } - }); + if (index === testData.length - 1) { + closeAndFinish(done, realtime); + } + }); - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } - /* Check that channel becomes failed if we get deltas when we don't have a vcdiff plugin */ - it('noPlugin', function (done) { - try { - var realtime = helper.AblyRealtime(); - var channel = realtime.channels.get('noPlugin', { params: { delta: 'vcdiff' } }); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - channel.once('failed', function (stateChange) { - try { - expect(stateChange.reason.code).to.equal(40019, 'Check error code'); - } catch (err) { + if (config.createRealtimeWithoutDeltaPlugin) { + /* Check that channel becomes failed if we get deltas when we don't have a vcdiff plugin */ + it('noPlugin', function (done) { + try { + var realtime = config.createRealtimeWithoutDeltaPlugin(helper.ablyClientOptions()); + var channel = realtime.channels.get('noPlugin', { params: { delta: 'vcdiff' } }); + + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); - return; } - closeAndFinish(done, realtime); - }); - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + channel.once('failed', function (stateChange) { + try { + expect(stateChange.reason.code).to.equal(40019, 'Check error code'); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + closeAndFinish(done, realtime); + }); + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } }); } diff --git a/test/support/browser_file_list.js b/test/support/browser_file_list.js index 5a4ceedc16..7350578e0a 100644 --- a/test/support/browser_file_list.js +++ b/test/support/browser_file_list.js @@ -8,7 +8,6 @@ window.__testFiles__.files = { 'build/ably.min.js': true, 'browser/lib/util/base64.js': true, 'node_modules/async/lib/async.js': true, - 'node_modules/@ably/vcdiff-decoder/dist/vcdiff-decoder.js': true, 'test/common/globals/environment.js': true, 'test/common/globals/named_dependencies.js': true, 'test/common/modules/client_module.js': true, diff --git a/test/support/browser_setup.js b/test/support/browser_setup.js index b668c84a45..0ce70489f1 100644 --- a/test/support/browser_setup.js +++ b/test/support/browser_setup.js @@ -58,9 +58,6 @@ require([(baseUrl + '/test/common/globals/named_dependencies.js').replace('//', 'browser-base64': { exports: 'Base64', }, - 'vcdiff-decoder': { - exports: 'vcdiffDecoder', - }, }, // dynamically load all test files