From 88e141a5c70254b645a2b07445a49a32914f6f74 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Mon, 20 Apr 2020 15:13:06 -0700 Subject: [PATCH] Warn when using qgrid > 1.1.1 (#11280) For #11245 * Warn when using qgrid version > 1.1.1 * For now hardcoded to check only qgrid version 1.1.1. * Should be easy enough to add others, didn't want to make it too generic. --- news/2 Fixes/11245.md | 1 + package.nls.json | 3 +- src/client/common/utils/localize.ts | 4 ++ src/client/datascience/constants.ts | 1 + .../interactiveWindowTypes.ts | 7 ++- .../interactive-common/synchronization.ts | 2 + .../ipywidgets/ipywidgetHandler.ts | 15 +++++- src/client/telemetry/index.ts | 4 ++ .../history-react/redux/reducers/index.ts | 1 + .../redux/reducers/commonEffects.ts | 39 ++++++++++++++- .../redux/reducers/types.ts | 6 +++ src/datascience-ui/ipywidgets/container.tsx | 45 +++++++++++++++-- .../ipywidgets/incompatibleWidgetHandler.ts | 47 ++++++++++++++++++ .../native-editor/redux/reducers/index.ts | 1 + .../incompatibleWidgetHandler.unit.test.ts | 49 +++++++++++++++++++ 15 files changed, 215 insertions(+), 10 deletions(-) create mode 100644 news/2 Fixes/11245.md create mode 100644 src/datascience-ui/ipywidgets/incompatibleWidgetHandler.ts create mode 100644 src/test/datascience/ipywidgets/incompatibleWidgetHandler.unit.test.ts diff --git a/news/2 Fixes/11245.md b/news/2 Fixes/11245.md new file mode 100644 index 000000000000..e15d31a3a9ce --- /dev/null +++ b/news/2 Fixes/11245.md @@ -0,0 +1 @@ +Warn when using a version of the widget `qgrid` greater than `1.1.1` with the recommendation to downgrade to `1.1.1`. diff --git a/package.nls.json b/package.nls.json index 72a5f8b1d19f..fecd902a57aa 100644 --- a/package.nls.json +++ b/package.nls.json @@ -470,5 +470,6 @@ "DataScience.loadThirdPartyWidgetScriptsPostEnabled": "Please restart the Kernel when changing the setting 'python.dataScience.widgetScriptSources'.", "DataScience.enableCDNForWidgetsSetting": "Widgets require us to download supporting files from a 3rd party website. Click here to enable this or click here for more information. (Error loading {0}:{1}).", "DataScience.widgetScriptNotFoundOnCDNWidgetMightNotWork": "Unable to load a compatible version of the widget '{0}'. Expected behavior may be affected.", - "DataScience.unhandledMessage": "Unhandled kernel message from a widget: {0} : {1}" + "DataScience.unhandledMessage": "Unhandled kernel message from a widget: {0} : {1}", + "DataScience.qgridWidgetScriptVersionCompatibilityWarning": "Unable to load a compatible version of the widget 'qgrid'. Consider downgrading to version 1.1.1." } diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index 6ecfe8cf03b5..d6a61f4e6f88 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -860,6 +860,10 @@ export namespace DataScience { 'DataScience.widgetScriptNotFoundOnCDNWidgetMightNotWork', "Unable to load a compatible version of the widget '{0}'. Expected behavior may be affected." ); + export const qgridWidgetScriptVersionCompatibilityWarning = localize( + 'DataScience.qgridWidgetScriptVersionCompatibilityWarning', + "Unable to load a compatible version of the widget 'qgrid'. Consider downgrading to version 1.1.1." + ); } export namespace DebugConfigStrings { diff --git a/src/client/datascience/constants.ts b/src/client/datascience/constants.ts index 954a7f8edb42..f9e3282e9456 100644 --- a/src/client/datascience/constants.ts +++ b/src/client/datascience/constants.ts @@ -292,6 +292,7 @@ export enum Telemetry { ZMQNotSupported = 'DATASCIENCE.ZMQ_NATIVE_BINARIES_NOT_LOADING', IPyWidgetLoadSuccess = 'DS_INTERNAL.IPYWIDGET_LOAD_SUCCESS', IPyWidgetLoadFailure = 'DS_INTERNAL.IPYWIDGET_LOAD_FAILURE', + IPyWidgetWidgetVersionNotSupportedLoadFailure = 'DS_INTERNAL.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED_LOAD_FAILURE', IPyWidgetLoadDisabled = 'DS_INTERNAL.IPYWIDGET_LOAD_DISABLED', HashedIPyWidgetNameUsed = 'DS_INTERNAL.IPYWIDGET_USED_BY_USER', HashedIPyWidgetNameDiscovered = 'DS_INTERNAL.IPYWIDGET_DISCOVERED', diff --git a/src/client/datascience/interactive-common/interactiveWindowTypes.ts b/src/client/datascience/interactive-common/interactiveWindowTypes.ts index ebd4735b08a8..7c82b58a9547 100644 --- a/src/client/datascience/interactive-common/interactiveWindowTypes.ts +++ b/src/client/datascience/interactive-common/interactiveWindowTypes.ts @@ -10,7 +10,8 @@ import { CommonActionType, IAddCellAction, ILoadIPyWidgetClassFailureAction, - LoadIPyWidgetClassLoadAction + LoadIPyWidgetClassLoadAction, + NotifyIPyWidgeWidgetVersionNotSupportedAction } from '../../../datascience-ui/interactive-common/redux/reducers/types'; import { PythonInterpreter } from '../../interpreter/contracts'; import { WidgetScriptSource } from '../ipywidgets/types'; @@ -115,7 +116,8 @@ export enum InteractiveWindowMessages { IPyWidgetLoadSuccess = 'ipywidget_load_success', IPyWidgetLoadFailure = 'ipywidget_load_failure', IPyWidgetRenderFailure = 'ipywidget_render_failure', - IPyWidgetUnhandledKernelMessage = 'ipywidget_unhandled_kernel_message' + IPyWidgetUnhandledKernelMessage = 'ipywidget_unhandled_kernel_message', + IPyWidgetWidgetVersionNotSupported = 'ipywidget_widget_version_not_supported' } export enum IPyWidgetMessages { @@ -607,6 +609,7 @@ export class IInteractiveWindowMapping { public [InteractiveWindowMessages.UpdateDisplayData]: KernelMessage.IUpdateDisplayDataMsg; public [InteractiveWindowMessages.IPyWidgetLoadSuccess]: LoadIPyWidgetClassLoadAction; public [InteractiveWindowMessages.IPyWidgetLoadFailure]: ILoadIPyWidgetClassFailureAction; + public [InteractiveWindowMessages.IPyWidgetWidgetVersionNotSupported]: NotifyIPyWidgeWidgetVersionNotSupportedAction; public [InteractiveWindowMessages.ConvertUriForUseInWebViewRequest]: Uri; public [InteractiveWindowMessages.ConvertUriForUseInWebViewResponse]: { request: Uri; response: Uri }; public [InteractiveWindowMessages.IPyWidgetRenderFailure]: Error; diff --git a/src/client/datascience/interactive-common/synchronization.ts b/src/client/datascience/interactive-common/synchronization.ts index 3ea62c88d253..7203a1bdc465 100644 --- a/src/client/datascience/interactive-common/synchronization.ts +++ b/src/client/datascience/interactive-common/synchronization.ts @@ -87,6 +87,7 @@ const messageWithMessageTypes: MessageMapping & Messa [CommonActionType.FOCUS_INPUT]: MessageType.other, [CommonActionType.LOAD_IPYWIDGET_CLASS_SUCCESS]: MessageType.other, [CommonActionType.LOAD_IPYWIDGET_CLASS_FAILURE]: MessageType.other, + [CommonActionType.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED]: MessageType.other, [CommonActionType.IPYWIDGET_RENDER_FAILURE]: MessageType.other, // Types from InteractiveWindowMessages @@ -117,6 +118,7 @@ const messageWithMessageTypes: MessageMapping & Messa [InteractiveWindowMessages.IPyWidgetLoadFailure]: MessageType.other, [InteractiveWindowMessages.IPyWidgetRenderFailure]: MessageType.other, [InteractiveWindowMessages.IPyWidgetUnhandledKernelMessage]: MessageType.other, + [InteractiveWindowMessages.IPyWidgetWidgetVersionNotSupported]: MessageType.other, [InteractiveWindowMessages.LoadAllCells]: MessageType.other, [InteractiveWindowMessages.LoadAllCellsComplete]: MessageType.other, [InteractiveWindowMessages.LoadOnigasmAssemblyRequest]: MessageType.other, diff --git a/src/client/datascience/ipywidgets/ipywidgetHandler.ts b/src/client/datascience/ipywidgets/ipywidgetHandler.ts index e26d2539e6ed..100dda03092b 100644 --- a/src/client/datascience/ipywidgets/ipywidgetHandler.ts +++ b/src/client/datascience/ipywidgets/ipywidgetHandler.ts @@ -9,7 +9,8 @@ import stripAnsi from 'strip-ansi'; import { Event, EventEmitter, Uri } from 'vscode'; import { ILoadIPyWidgetClassFailureAction, - LoadIPyWidgetClassLoadAction + LoadIPyWidgetClassLoadAction, + NotifyIPyWidgeWidgetVersionNotSupportedAction } from '../../../datascience-ui/interactive-common/redux/reducers/types'; import { EnableIPyWidgets } from '../../common/experimentGroups'; import { traceError, traceInfo } from '../../common/logger'; @@ -75,6 +76,8 @@ export class IPyWidgetHandler implements IInteractiveWindowListener { this.sendLoadSucceededTelemetry(payload); } else if (message === InteractiveWindowMessages.IPyWidgetLoadFailure) { this.sendLoadFailureTelemetry(payload); + } else if (message === InteractiveWindowMessages.IPyWidgetWidgetVersionNotSupported) { + this.sendUnsupportedWidgetVersionFailureTelemetry(payload); } else if (message === InteractiveWindowMessages.IPyWidgetRenderFailure) { this.sendRenderFailureTelemetry(payload); } else if (message === InteractiveWindowMessages.IPyWidgetUnhandledKernelMessage) { @@ -111,6 +114,16 @@ export class IPyWidgetHandler implements IInteractiveWindowListener { // do nothing on failure } } + private sendUnsupportedWidgetVersionFailureTelemetry(payload: NotifyIPyWidgeWidgetVersionNotSupportedAction) { + try { + sendTelemetryEvent(Telemetry.IPyWidgetWidgetVersionNotSupportedLoadFailure, 0, { + moduleHash: this.hash(payload.moduleName), + moduleVersion: payload.moduleVersion + }); + } catch { + // do nothing on failure + } + } private sendRenderFailureTelemetry(payload: Error) { try { traceError('Error rendering a widget: ', payload); diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 240d88f199b4..2503ebeb8b8d 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1992,6 +1992,10 @@ export interface IEventNamePropertyMapping { // Whether we timedout getting the source of the script (fetching script source in extension code). timedout: boolean; }; + /** + * Telemetry event sent when an ipywidget version that is not supported is used & we have trapped this and warned the user abou it. + */ + [Telemetry.IPyWidgetWidgetVersionNotSupportedLoadFailure]: { moduleHash: string; moduleVersion: string }; /** * Telemetry event sent when an loading of 3rd party ipywidget JS scripts from 3rd party source has been disabled. */ diff --git a/src/datascience-ui/history-react/redux/reducers/index.ts b/src/datascience-ui/history-react/redux/reducers/index.ts index 533397435765..a7f68811f3c1 100644 --- a/src/datascience-ui/history-react/redux/reducers/index.ts +++ b/src/datascience-ui/history-react/redux/reducers/index.ts @@ -41,6 +41,7 @@ export const reducerMap: Partial = { [CommonActionType.FOCUS_INPUT]: CommonEffects.focusInput, [CommonActionType.LOAD_IPYWIDGET_CLASS_SUCCESS]: CommonEffects.handleLoadIPyWidgetClassSuccess, [CommonActionType.LOAD_IPYWIDGET_CLASS_FAILURE]: CommonEffects.handleLoadIPyWidgetClassFailure, + [CommonActionType.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED]: CommonEffects.notifyAboutUnsupportedWidgetVersions, [CommonActionType.IPYWIDGET_RENDER_FAILURE]: CommonEffects.handleIPyWidgetRenderFailure, // Messages from the webview (some are ignored) diff --git a/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts b/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts index 814a01c2c550..7de7fbae8e6f 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/commonEffects.ts @@ -18,7 +18,8 @@ import { CommonReducerArg, ILoadIPyWidgetClassFailureAction, IOpenSettingsAction, - LoadIPyWidgetClassLoadAction + LoadIPyWidgetClassLoadAction, + NotifyIPyWidgeWidgetVersionNotSupportedAction } from './types'; export namespace CommonEffects { @@ -258,6 +259,42 @@ export namespace CommonEffects { return arg.prevState; } } + export function notifyAboutUnsupportedWidgetVersions( + arg: CommonReducerArg + ): IMainState { + // Find the first currently executing cell and add an error to its output + let index = arg.prevState.cellVMs.findIndex((c) => c.cell.state === CellState.executing); + + // If there isn't one, then find the latest that matches the current execution count. + if (index < 0) { + index = arg.prevState.cellVMs.findIndex( + (c) => c.cell.data.execution_count === arg.prevState.currentExecutionCount + ); + } + if (index >= 0 && arg.prevState.cellVMs[index].cell.data.cell_type === 'code') { + const newVMs = [...arg.prevState.cellVMs]; + const current = arg.prevState.cellVMs[index]; + + const errorMessage = getLocString( + 'DataScience.qgridWidgetScriptVersionCompatibilityWarning', + "Unable to load a compatible version of the widget 'qgrid'. Consider downgrading to version 1.1.1." + ); + newVMs[index] = Helpers.asCellViewModel({ + ...current, + uiSideError: errorMessage + }); + + // Make sure to tell the extension so it can log telemetry. + postActionToExtension(arg, InteractiveWindowMessages.IPyWidgetWidgetVersionNotSupported, arg.payload.data); + + return { + ...arg.prevState, + cellVMs: newVMs + }; + } else { + return arg.prevState; + } + } export function handleIPyWidgetRenderFailure(arg: CommonReducerArg): IMainState { // Make sure to tell the extension so it can log telemetry. postActionToExtension(arg, InteractiveWindowMessages.IPyWidgetRenderFailure, arg.payload.data); diff --git a/src/datascience-ui/interactive-common/redux/reducers/types.ts b/src/datascience-ui/interactive-common/redux/reducers/types.ts index 2a5f8d5e6910..9fddb76d7570 100644 --- a/src/datascience-ui/interactive-common/redux/reducers/types.ts +++ b/src/datascience-ui/interactive-common/redux/reducers/types.ts @@ -58,6 +58,7 @@ export enum CommonActionType { IPYWIDGET_RENDER_FAILURE = 'action.ipywidget_render_failure', LOAD_IPYWIDGET_CLASS_SUCCESS = 'action.load_ipywidget_class_success', LOAD_IPYWIDGET_CLASS_FAILURE = 'action.load_ipywidget_class_failure', + IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED = 'action.ipywidget_widget_version_not_supported', LOADED_ALL_CELLS = 'action.loaded_all_cells', LINK_CLICK = 'action.link_click', MOVE_CELL_DOWN = 'action.move_cell_down', @@ -134,6 +135,7 @@ export type CommonActionTypeMapping = { [CommonActionType.FOCUS_INPUT]: never | undefined; [CommonActionType.LOAD_IPYWIDGET_CLASS_SUCCESS]: LoadIPyWidgetClassLoadAction; [CommonActionType.LOAD_IPYWIDGET_CLASS_FAILURE]: ILoadIPyWidgetClassFailureAction; + [CommonActionType.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED]: NotifyIPyWidgeWidgetVersionNotSupportedAction; [CommonActionType.IPYWIDGET_RENDER_FAILURE]: Error; }; @@ -232,5 +234,9 @@ export type LoadIPyWidgetClassLoadAction = { moduleName: string; moduleVersion: string; }; +export type NotifyIPyWidgeWidgetVersionNotSupportedAction = { + moduleName: 'qgrid'; + moduleVersion: string; +}; export type CommonAction = ActionWithPayload; diff --git a/src/datascience-ui/ipywidgets/container.tsx b/src/datascience-ui/ipywidgets/container.tsx index 1072ccf33849..ef5fed6a19d5 100644 --- a/src/datascience-ui/ipywidgets/container.tsx +++ b/src/datascience-ui/ipywidgets/container.tsx @@ -19,10 +19,12 @@ import { CommonAction, CommonActionType, ILoadIPyWidgetClassFailureAction, - LoadIPyWidgetClassLoadAction + LoadIPyWidgetClassLoadAction, + NotifyIPyWidgeWidgetVersionNotSupportedAction } from '../interactive-common/redux/reducers/types'; import { IStore } from '../interactive-common/redux/store'; import { PostOffice } from '../react-common/postOffice'; +import { warnAboutWidgetVersionsThatAreNotSupported } from './incompatibleWidgetHandler'; import { WidgetManager } from './manager'; import { registerScripts } from './requirejsRegistry'; @@ -38,6 +40,7 @@ export class WidgetManagerComponent extends React.Component { string, { deferred: Deferred; timer: NodeJS.Timeout | number | undefined } >(); + private readonly registeredWidgetSources = new Map(); private timedoutWaitingForWidgetsToGetLoaded?: boolean; private widgetsCanLoadFromCDN: boolean = false; private readonly loaderSettings = { @@ -78,6 +81,7 @@ export class WidgetManagerComponent extends React.Component { // This happens when we have restarted a kernel. // If user changed the kernel, then some widgets might exist now and some might now. this.widgetSourceRequests.clear(); + this.registeredWidgetSources.clear(); } return true; } @@ -108,6 +112,7 @@ export class WidgetManagerComponent extends React.Component { // Now resolve promises (anything that was waiting for modules to get registered can carry on). sources.forEach((source) => { + this.registeredWidgetSources.set(source.moduleName, source); // We have fetched the script sources for all of these modules. // In some cases we might not have the source, meaning we don't have it or couldn't find it. let request = this.widgetSourceRequests.get(source.moduleName); @@ -167,6 +172,21 @@ export class WidgetManagerComponent extends React.Component { } }; } + private createWidgetVersionNotSupportedErrorAction( + moduleName: 'qgrid', + moduleVersion: string + ): CommonAction { + return { + type: CommonActionType.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED, + payload: { + messageDirection: 'incoming', + data: { + moduleName, + moduleVersion + } + } + }; + } private async handleLoadError( className: string, moduleName: string, @@ -233,10 +253,25 @@ export class WidgetManagerComponent extends React.Component { { moduleName, moduleVersion } ); - return request.deferred.promise.catch((ex) => - // tslint:disable-next-line: no-console - console.error(`Failed to load Widget Script from Extension for for ${moduleName}, ${moduleVersion}`, ex) - ); + return request.deferred.promise + .then(() => { + const widgetSource = this.registeredWidgetSources.get(moduleName); + if (widgetSource) { + warnAboutWidgetVersionsThatAreNotSupported( + widgetSource, + moduleVersion, + this.widgetsCanLoadFromCDN, + (info) => + this.props.store.dispatch( + this.createWidgetVersionNotSupportedErrorAction(info.moduleName, info.moduleVersion) + ) + ); + } + }) + .catch((ex) => + // tslint:disable-next-line: no-console + console.error(`Failed to load Widget Script from Extension for for ${moduleName}, ${moduleVersion}`, ex) + ); } private handleLoadSuccess(className: string, moduleName: string, moduleVersion: string) { diff --git a/src/datascience-ui/ipywidgets/incompatibleWidgetHandler.ts b/src/datascience-ui/ipywidgets/incompatibleWidgetHandler.ts new file mode 100644 index 000000000000..a6d3ce4e1207 --- /dev/null +++ b/src/datascience-ui/ipywidgets/incompatibleWidgetHandler.ts @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import * as semver from 'semver'; +import { WidgetScriptSource } from '../../client/datascience/ipywidgets/types'; +const supportedVersionOfQgrid = '1.1.1'; +const qgridModuleName = 'qgrid'; + +/** + * For now only warns about qgrid. + * Warn user about qgrid versions > 1.1.1 (we know CDN isn't available for newer versions and local widget source will not work). + * Recommend to downgrade to 1.1.1. + * Returns `true` if a warning has been displayed. + */ +export function warnAboutWidgetVersionsThatAreNotSupported( + widgetSource: WidgetScriptSource, + moduleVersion: string, + cdnSupported: boolean, + errorDispatcher: (info: { moduleName: typeof qgridModuleName; moduleVersion: string }) => void +) { + // if widget exists on CDN or CDN is disabled, get out. + if (widgetSource.source === 'cdn' || !cdnSupported) { + return false; + } + // Warn about qrid. + if (widgetSource.moduleName !== qgridModuleName) { + return false; + } + // We're only interested in versions > 1.1.1. + try { + // If we have an exact version, & if that is <= 1.1.1, then no warning needs to be displayed. + if (!moduleVersion.startsWith('^') && semver.compare(moduleVersion, supportedVersionOfQgrid) <= 0) { + return false; + } + // If we have a version range, then check the range. + // Basically if our minimum version 1.1.1 is met, then nothing to do. + // Eg. requesting script source for version `^1.3.0`. + if (moduleVersion.startsWith('^') && semver.satisfies(supportedVersionOfQgrid, moduleVersion)) { + return false; + } + } catch { + return false; + } + errorDispatcher({ moduleName: widgetSource.moduleName, moduleVersion }); +} diff --git a/src/datascience-ui/native-editor/redux/reducers/index.ts b/src/datascience-ui/native-editor/redux/reducers/index.ts index 049954ac03c8..d12ac9167a58 100644 --- a/src/datascience-ui/native-editor/redux/reducers/index.ts +++ b/src/datascience-ui/native-editor/redux/reducers/index.ts @@ -60,6 +60,7 @@ export const reducerMap: Partial = { [CommonActionType.UNMOUNT]: Creation.unmount, [CommonActionType.LOAD_IPYWIDGET_CLASS_SUCCESS]: CommonEffects.handleLoadIPyWidgetClassSuccess, [CommonActionType.LOAD_IPYWIDGET_CLASS_FAILURE]: CommonEffects.handleLoadIPyWidgetClassFailure, + [CommonActionType.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED]: CommonEffects.notifyAboutUnsupportedWidgetVersions, // Messages from the webview (some are ignored) [InteractiveWindowMessages.StartCell]: Creation.startCell, diff --git a/src/test/datascience/ipywidgets/incompatibleWidgetHandler.unit.test.ts b/src/test/datascience/ipywidgets/incompatibleWidgetHandler.unit.test.ts new file mode 100644 index 000000000000..48f8c2170fda --- /dev/null +++ b/src/test/datascience/ipywidgets/incompatibleWidgetHandler.unit.test.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { assert } from 'chai'; +import { warnAboutWidgetVersionsThatAreNotSupported } from '../../../datascience-ui/ipywidgets/incompatibleWidgetHandler'; + +// tslint:disable: max-func-body-length no-any +suite('Data Science - Incompatible Widgets', () => { + suite('Using qgrid widget with CDN turned on', () => { + async function testLoadingQgrid(versionToLoad: string, warningExpectedToBeDisplayed: boolean) { + let warningDisplayed = false; + warnAboutWidgetVersionsThatAreNotSupported( + { moduleName: 'qgrid' }, + versionToLoad, + true, + () => (warningDisplayed = true) + ); + + assert.equal(warningDisplayed, warningExpectedToBeDisplayed); + } + test('Widget script is not found for qgrid@1.1.0, then do not display a warning', async () => { + // This test just ensures we never display warnings for 1.1.0. + // This will never happen as the file exists on CDN. + // Hence gurantees that we'll not display when not required. + await testLoadingQgrid('1.1.0', false); + }); + test('Widget script is not found for qgrid@1.1.1, then do not display a warning', async () => { + // This test just ensures we never display warnings for 1.1.0. + // This will never happen as the file exists on CDN. + // Hence gurantees that we'll not display when not required. + await testLoadingQgrid('1.1.1', false); + }); + test('Widget script is not found for qgrid@1.1.2, then display a warning', async () => { + // We know there are no scripts on CDN for > 1.1.1 + await testLoadingQgrid('1.1.2', true); + }); + test('Widget script is not found for qgrid@^1.1.2, then display a warning', async () => { + // We know there are no scripts on CDN for > 1.1.1 + await testLoadingQgrid('^1.1.2', true); + }); + test('Widget script is not found for qgrid@1.3.0, then display a warning', async () => { + // We know there are no scripts on CDN for > 1.1.1 + await testLoadingQgrid('1.3.0', true); + }); + test('Widget script is not found for qgrid@^1.3.0, then display a warning', async () => { + // We know there are no scripts on CDN for > 1.1.1 + await testLoadingQgrid('^1.3.0', true); + }); + }); +});