diff --git a/.eslintignore b/.eslintignore index 9f74e282..a6796e04 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,5 @@ # Task 512388: Fix eslint warnings and errors in tests -test/* \ No newline at end of file +/node_modules/* +demo/* +/**/*.js +dist/* \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 2eaee33e..4240ccee 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -21,14 +21,9 @@ module.exports = { ], "rules": { "@typescript-eslint/adjacent-overload-signatures": "warn", - "@typescript-eslint/array-type": [ - "warn", - { - "default": "array-simple" - } - ], + "@typescript-eslint/array-type": "off", "@typescript-eslint/await-thenable": "warn", - "@typescript-eslint/ban-ts-comment": "warn", + "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-types": [ "warn", { @@ -36,9 +31,8 @@ module.exports = { "Object": { "message": "Avoid using the `Object` type. Did you mean `object`?" }, - "Function": { - "message": "Avoid using the `Function` type. Prefer a specific function type, like `() => void`." - }, + "Function": false, + "object": false, "Boolean": { "message": "Avoid using the `Boolean` type. Did you mean `boolean`?" }, @@ -54,16 +48,18 @@ module.exports = { } } ], - "@typescript-eslint/consistent-type-assertions": "warn", "@typescript-eslint/consistent-type-definitions": "warn", - "@typescript-eslint/dot-notation": "warn", + "@typescript-eslint/dot-notation": "off", "@typescript-eslint/explicit-member-accessibility": [ "off", { "accessibility": "explicit" } ], - "@typescript-eslint/explicit-module-boundary-types": "warn", + "@typescript-eslint/explicit-module-boundary-types": [ + "warn", + { "allowArgumentsExplicitlyTypedAsAny": true } + ], "@typescript-eslint/indent": [ "warn", 2, @@ -90,6 +86,13 @@ module.exports = { } } ], + "@typescript-eslint/explicit-function-return-type": [ + "error", + { + "allowExpressions": true, + "allowDirectConstAssertionInArrowFunctions": true + } + ], "@typescript-eslint/member-ordering": "off", "@typescript-eslint/naming-convention": "off", "@typescript-eslint/no-array-constructor": "warn", @@ -98,29 +101,33 @@ module.exports = { "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-extra-non-null-assertion": "warn", "@typescript-eslint/no-extra-semi": "warn", - "@typescript-eslint/no-floating-promises": "warn", + "@typescript-eslint/no-floating-promises": "off", "@typescript-eslint/no-for-in-array": "warn", "@typescript-eslint/no-implied-eval": "warn", - "@typescript-eslint/no-inferrable-types": "warn", + "@typescript-eslint/no-inferrable-types": "off", "@typescript-eslint/no-misused-new": "warn", - "@typescript-eslint/no-misused-promises": "warn", + "@typescript-eslint/no-misused-promises": "off", "@typescript-eslint/no-namespace": "warn", "@typescript-eslint/no-non-null-asserted-optional-chain": "warn", "@typescript-eslint/no-non-null-assertion": "warn", "@typescript-eslint/no-parameter-properties": "off", "@typescript-eslint/no-this-alias": "warn", "@typescript-eslint/no-unnecessary-type-assertion": "warn", - "@typescript-eslint/no-unsafe-assignment": "warn", - "@typescript-eslint/no-unsafe-call": "warn", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-return": "warn", + "@typescript-eslint/no-unsafe-return": "off", "@typescript-eslint/no-unused-expressions": "warn", - "@typescript-eslint/no-unused-vars": "warn", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "args": "after-used", "argsIgnorePattern": "^_" + } + ], "@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/no-var-requires": "warn", "@typescript-eslint/prefer-as-const": "warn", "@typescript-eslint/prefer-for-of": "warn", - "@typescript-eslint/prefer-function-type": "warn", "@typescript-eslint/prefer-namespace-keyword": "warn", "@typescript-eslint/prefer-regexp-exec": "off", "@typescript-eslint/quotes": [ @@ -131,10 +138,9 @@ module.exports = { ], "@typescript-eslint/require-await": "warn", "@typescript-eslint/restrict-plus-operands": "warn", - "@typescript-eslint/restrict-template-expressions": "warn", + "@typescript-eslint/restrict-template-expressions": "off", "@typescript-eslint/semi": [ - "warn", - "always" + "error", ], "@typescript-eslint/triple-slash-reference": [ "warn", @@ -145,13 +151,9 @@ module.exports = { } ], "@typescript-eslint/type-annotation-spacing": "warn", - "@typescript-eslint/unbound-method": "warn", + "@typescript-eslint/unbound-method": "off", "@typescript-eslint/unified-signatures": "warn", - "arrow-body-style": "warn", - "arrow-parens": [ - "warn", - "always" - ], + "arrow-parens": "off", "brace-style": [ "off", "1tbs" @@ -159,7 +161,6 @@ module.exports = { "comma-dangle": "off", "complexity": "off", "constructor-super": "warn", - "curly": "warn", "eol-last": "warn", "eqeqeq": [ "warn", @@ -178,7 +179,7 @@ module.exports = { "Undefined", ], "id-match": "warn", - "import/order": "warn", + "import/order": "off", "jsdoc/check-alignment": "warn", "jsdoc/check-indentation": "warn", "jsdoc/newline-after-description": "warn", @@ -189,7 +190,6 @@ module.exports = { "max-len": "off", "new-parens": "warn", "no-array-constructor": "off", - "no-bitwise": "warn", "no-caller": "warn", "no-cond-assign": "warn", "no-console": "off", @@ -201,29 +201,18 @@ module.exports = { "no-fallthrough": "off", "no-implied-eval": "off", "no-invalid-this": "off", - "no-multiple-empty-lines": "warn", + "no-multiple-empty-lines": ["error", { "max": 1 }], "no-new-wrappers": "warn", - "no-shadow": [ - "warn", - { - "hoist": "all" - } - ], - "no-throw-literal": "warn", + "no-shadow": "off", "no-trailing-spaces": "warn", "no-undef-init": "warn", - "no-underscore-dangle": "warn", + "no-underscore-dangle": "off", "no-unsafe-finally": "warn", "no-unused-labels": "warn", - "no-unused-vars": "off", "no-var": "warn", - "object-shorthand": ["warn", "never"], - "one-var": [ - "warn", - "never" - ], - "prefer-arrow/prefer-arrow-functions": "off", - "prefer-const": "warn", + "object-shorthand": "off", + "one-var": "off", + "prefer-const": "off", "prefer-rest-params": "warn", "quote-props": [ "warn", @@ -231,14 +220,7 @@ module.exports = { ], "radix": "warn", "require-await": "off", - "space-before-function-paren": [ - "warn", - { - "anonymous": "never", - "asyncArrow": "always", - "named": "never" - } - ], + "space-before-function-paren": "off", "spaced-comment": [ "warn", "always", diff --git a/dist/powerbi-client.d.ts b/dist/powerbi-client.d.ts index 2e9082b2..adfe29cf 100644 --- a/dist/powerbi-client.d.ts +++ b/dist/powerbi-client.d.ts @@ -1,4 +1,4 @@ -// powerbi-client v2.18.6 +// powerbi-client v2.18.7 // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. declare module "config" { @@ -780,7 +780,7 @@ declare module "visualDescriptor" { } declare module "page" { import { IHttpPostMessageResponse } from 'http-post-message'; - import { DisplayOption, FiltersOperations, ICustomPageSize, IFilter, IVisual, LayoutType, PageSizeType, SectionVisibility, VisualContainerDisplayMode } from 'powerbi-models'; + import { DisplayOption, FiltersOperations, ICustomPageSize, IFilter, IVisual, LayoutType, PageSizeType, SectionVisibility, VisualContainerDisplayMode, IPageBackground, IPageWallpaper } from 'powerbi-models'; import { IFilterable } from "ifilterable"; import { IReportNode } from "report"; import { VisualDescriptor } from "visualDescriptor"; @@ -853,6 +853,18 @@ declare module "page" { * @type {ICustomPageSize} */ defaultDisplayOption: DisplayOption; + /** + * Page background color. + * + * @type {IPageBackground} + */ + background: IPageBackground; + /** + * Page wallpaper color. + * + * @type {IPageWallpaper} + */ + wallpaper: IPageWallpaper; /** * Creates an instance of a Power BI report page. * @@ -863,7 +875,7 @@ declare module "page" { * @param {SectionVisibility} [visibility] * @hidden */ - constructor(report: IReportNode, name: string, displayName?: string, isActivePage?: boolean, visibility?: SectionVisibility, defaultSize?: ICustomPageSize, defaultDisplayOption?: DisplayOption, mobileSize?: ICustomPageSize); + constructor(report: IReportNode, name: string, displayName?: string, isActivePage?: boolean, visibility?: SectionVisibility, defaultSize?: ICustomPageSize, defaultDisplayOption?: DisplayOption, mobileSize?: ICustomPageSize, background?: IPageBackground, wallpaper?: IPageWallpaper); /** * Gets all page level filters within the report. * @@ -1368,6 +1380,14 @@ declare module "report" { * ``` */ resetTheme(): Promise; + /** + * get the theme of the report + * + * ```javascript + * report.getTheme(); + * ``` + */ + getTheme(): Promise; /** * Reset user's filters, slicers, and other data view changes to the default state of the report * @@ -1566,11 +1586,13 @@ declare module "report" { private isMobileSettings; /** * Return the current zoom level of the report. + * * @returns {Promise} */ getZoom(): Promise; /** * Sets the report's zoom level. + * * @param zoomLevel zoom level to set */ setZoom(zoomLevel: number): Promise; diff --git a/dist/powerbi.js b/dist/powerbi.js index 94daed7b..01fdc4be 100644 --- a/dist/powerbi.js +++ b/dist/powerbi.js @@ -1,4 +1,4 @@ -// powerbi-client v2.18.6 +// powerbi-client v2.18.7 // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. (function webpackUniversalModuleDefinition(root, factory) { @@ -290,7 +290,7 @@ return /******/ (function(modules) { // webpackBootstrap /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -// powerbi-models v1.9.5 +// powerbi-models v1.9.7 // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. (function webpackUniversalModuleDefinition(root, factory) { @@ -797,9 +797,6 @@ var AdvancedFilter = /** @class */ (function (_super) { else { extractedConditions = conditions; } - if (extractedConditions.length === 0) { - throw new Error("conditions must be a non-empty array. You passed: " + conditions); - } if (extractedConditions.length > 2) { throw new Error("AdvancedFilters may not have more than two conditions. You passed: " + conditions.length); } @@ -2915,7 +2912,7 @@ var AdvancedFilterValidator = /** @class */ (function (_super) { }, { field: "conditions", - validators: [validator_1.Validators.fieldRequiredValidator, validator_1.Validators.filterConditionsValidator] + validators: [validator_1.Validators.filterConditionsValidator] }, { field: "filterType", @@ -6990,7 +6987,7 @@ var BookmarksManager = /** @class */ (function () { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -7024,7 +7021,7 @@ var BookmarksManager = /** @class */ (function () { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } request = { @@ -7060,7 +7057,7 @@ var BookmarksManager = /** @class */ (function () { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } playBookmarkRequest = { @@ -7095,7 +7092,7 @@ var BookmarksManager = /** @class */ (function () { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } request = { @@ -7132,7 +7129,7 @@ var BookmarksManager = /** @class */ (function () { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } request = { @@ -7170,7 +7167,7 @@ exports.BookmarksManager = BookmarksManager; Object.defineProperty(exports, "__esModule", { value: true }); /** @ignore */ /** */ var config = { - version: '2.18.6', + version: '2.18.7', type: 'js' }; exports.default = config; @@ -7274,7 +7271,7 @@ var Create = /** @class */ (function (_super) { * Validate create report configuration. */ Create.prototype.validate = function (config) { - return (0, powerbi_models_1.validateCreateReport)(config); + return powerbi_models_1.validateCreateReport(config); }; /** * Handle config changes. @@ -7441,7 +7438,7 @@ var Dashboard = /** @class */ (function (_super) { */ Dashboard.prototype.validate = function (baseConfig) { var config = baseConfig; - var error = (0, powerbi_models_1.validateDashboardLoad)(config); + var error = powerbi_models_1.validateDashboardLoad(config); return error ? error : this.validatePageView(config.pageView); }; /** @@ -7563,7 +7560,7 @@ var Embed = /** @class */ (function () { function Embed(service, element, config, iframe, phasedRender, isBootstrap) { /** @hidden */ this.allowedEvents = []; - if ((0, util_1.autoAuthInEmbedUrl)(config.embedUrl)) { + if (util_1.autoAuthInEmbedUrl(config.embedUrl)) { throw new Error(errors_1.EmbedUrlNotSupported); } Array.prototype.push.apply(this.allowedEvents, Embed.allowedEvents); @@ -7749,7 +7746,7 @@ var Embed = /** @class */ (function () { sdkVersion: sdkConfig.default.version }; timeNow = new Date(); - if (this.lastLoadRequest && (0, util_1.getTimeDiffInMilliseconds)(this.lastLoadRequest, timeNow) < 100) { + if (this.lastLoadRequest && util_1.getTimeDiffInMilliseconds(this.lastLoadRequest, timeNow) < 100) { console.debug("Power BI SDK sent more than two /report/load requests in the last 100ms interval."); return [2 /*return*/]; } @@ -7794,7 +7791,7 @@ var Embed = /** @class */ (function () { var _this = this; var fakeEvent = { name: eventName, type: null, id: null, value: null }; if (handler) { - (0, util_1.remove)(function (eventHandler) { return eventHandler.test(fakeEvent) && (eventHandler.handle === handler); }, this.eventHandlers); + util_1.remove(function (eventHandler) { return eventHandler.test(fakeEvent) && (eventHandler.handle === handler); }, this.eventHandlers); this.element.removeEventListener(eventName, handler); } else { @@ -7802,7 +7799,7 @@ var Embed = /** @class */ (function () { .filter(function (eventHandler) { return eventHandler.test(fakeEvent); }); eventHandlersToRemove .forEach(function (eventHandlerToRemove) { - (0, util_1.remove)(function (eventHandler) { return eventHandler === eventHandlerToRemove; }, _this.eventHandlers); + util_1.remove(function (eventHandler) { return eventHandler === eventHandlerToRemove; }, _this.eventHandlers); _this.element.removeEventListener(eventName, eventHandlerToRemove.handle); }); } @@ -7908,14 +7905,14 @@ var Embed = /** @class */ (function () { var _this = this; var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; if (this.bootstrapConfig) { - this.config = (0, util_1.assign)({}, this.bootstrapConfig, config); + this.config = util_1.assign({}, this.bootstrapConfig, config); // reset bootstrapConfig because we do not want to merge it in re-embed scenario. this.bootstrapConfig = null; } else { // Copy config - important for multiple iframe scenario. // Otherwise, if a user uses the same config twice, same unique Id which will be used in different iframes. - this.config = (0, util_1.assign)({}, config); + this.config = util_1.assign({}, config); } this.config.embedUrl = this.getEmbedUrl(isBootstrap); this.config.groupId = this.getGroupId(); @@ -7944,7 +7941,7 @@ var Embed = /** @class */ (function () { var registerQueryCallback = !!((_k = this.config.eventHooks) === null || _k === void 0 ? void 0 : _k.applicationContextProvider); delete this.config.eventHooks; if (registerQueryCallback && this.embedtype === "report") - this.config.embedUrl = (0, util_1.addParamToUrl)(this.config.embedUrl, "registerQueryCallback", "true"); + this.config.embedUrl = util_1.addParamToUrl(this.config.embedUrl, "registerQueryCallback", "true"); this.configChanged(isBootstrap); }; /** @@ -7960,10 +7957,10 @@ var Embed = /** @class */ (function () { } var localeSettings = config.settings.localeSettings; if (localeSettings && localeSettings.language) { - this.config.embedUrl = (0, util_1.addParamToUrl)(this.config.embedUrl, 'language', localeSettings.language); + this.config.embedUrl = util_1.addParamToUrl(this.config.embedUrl, 'language', localeSettings.language); } if (localeSettings && localeSettings.formatLocale) { - this.config.embedUrl = (0, util_1.addParamToUrl)(this.config.embedUrl, 'formatLocale', localeSettings.formatLocale); + this.config.embedUrl = util_1.addParamToUrl(this.config.embedUrl, 'formatLocale', localeSettings.formatLocale); } }; /** @@ -8011,7 +8008,7 @@ var Embed = /** @class */ (function () { * @hidden */ Embed.prototype.getUniqueId = function () { - return this.config.uniqueId || this.element.getAttribute(Embed.nameAttribute) || (0, util_1.createRandomString)(); + return this.config.uniqueId || this.element.getAttribute(Embed.nameAttribute) || util_1.createRandomString(); }; /** * Gets the group ID from the first available location: options, embeddedUrl. @@ -8058,11 +8055,11 @@ var Embed = /** @class */ (function () { * * @hidden */ - Embed.prototype.setIframe = function (isLoad, phasedRender, isBootstrap, registerQueryCallback) { + Embed.prototype.setIframe = function (isLoad, phasedRender, isBootstrap) { var _this = this; if (!this.iframe) { var iframeContent = document.createElement("iframe"); - var embedUrl = this.config.uniqueId ? (0, util_1.addParamToUrl)(this.config.embedUrl, 'uid', this.config.uniqueId) : this.config.embedUrl; + var embedUrl = this.config.uniqueId ? util_1.addParamToUrl(this.config.embedUrl, 'uid', this.config.uniqueId) : this.config.embedUrl; iframeContent.style.width = '100%'; iframeContent.style.height = '100%'; iframeContent.setAttribute("src", embedUrl); @@ -8343,7 +8340,7 @@ var Page = /** @class */ (function () { * @param {SectionVisibility} [visibility] * @hidden */ - function Page(report, name, displayName, isActivePage, visibility, defaultSize, defaultDisplayOption, mobileSize) { + function Page(report, name, displayName, isActivePage, visibility, defaultSize, defaultDisplayOption, mobileSize, background, wallpaper) { this.report = report; this.name = name; this.displayName = displayName; @@ -8352,6 +8349,8 @@ var Page = /** @class */ (function () { this.defaultSize = defaultSize; this.mobileSize = mobileSize; this.defaultDisplayOption = defaultDisplayOption; + this.background = background; + this.wallpaper = wallpaper; } /** * Gets all page level filters within the report. @@ -8574,7 +8573,7 @@ var Page = /** @class */ (function () { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.report.config.embedUrl)) { + if (util_1.isRDLEmbed(this.report.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -8612,7 +8611,7 @@ var Page = /** @class */ (function () { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.report.config.embedUrl)) { + if (util_1.isRDLEmbed(this.report.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -8747,7 +8746,7 @@ var Page = /** @class */ (function () { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.report.config.embedUrl)) { + if (util_1.isRDLEmbed(this.report.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -8783,7 +8782,7 @@ var Page = /** @class */ (function () { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.report.config.embedUrl)) { + if (util_1.isRDLEmbed(this.report.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } layoutTypeEnum = powerbi_models_1.LayoutType[layoutType]; @@ -9000,7 +8999,7 @@ var Qna = /** @class */ (function (_super) { * Validate load configuration. */ Qna.prototype.validate = function (config) { - return (0, powerbi_models_1.validateLoadQnaConfiguration)(config); + return powerbi_models_1.validateLoadQnaConfiguration(config); }; /** @hidden */ Qna.type = "Qna"; @@ -9073,14 +9072,10 @@ var __generator = (this && this.__generator) || function (thisArg, body) { if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; -var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { - if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { - if (ar || !(i in from)) { - if (!ar) ar = Array.prototype.slice.call(from, 0, i); - ar[i] = from[i]; - } - } - return to.concat(ar || Array.prototype.slice.call(from)); +var __spreadArray = (this && this.__spreadArray) || function (to, from) { + for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) + to[j] = from[i]; + return to; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Report = void 0; @@ -9291,7 +9286,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -9355,7 +9350,7 @@ var Report = /** @class */ (function (_super) { Report.prototype.removeFilters = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } return [2 /*return*/, this.updateFilters(powerbi_models_1.FiltersOperations.RemoveAll)]; @@ -9385,7 +9380,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -9433,7 +9428,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -9443,7 +9438,7 @@ var Report = /** @class */ (function (_super) { case 2: response = _a.sent(); return [2 /*return*/, response.body - .map(function (page) { return new page_1.Page(_this, page.name, page.displayName, page.isActive, page.visibility, page.defaultSize, page.defaultDisplayOption, page.mobileSize); })]; + .map(function (page) { return new page_1.Page(_this, page.name, page.displayName, page.isActive, page.visibility, page.defaultSize, page.defaultDisplayOption, page.mobileSize, page.background, page.wallpaper); })]; case 3: response_8 = _a.sent(); throw response_8.body; @@ -9471,7 +9466,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -9484,7 +9479,7 @@ var Report = /** @class */ (function (_super) { if (!page) { return [2 /*return*/, Promise.reject(powerbi_models_1.CommonErrorCodes.NotFound)]; } - return [2 /*return*/, new page_1.Page(this, page.name, page.displayName, page.isActive, page.visibility, page.defaultSize, page.defaultDisplayOption, page.mobileSize)]; + return [2 /*return*/, new page_1.Page(this, page.name, page.displayName, page.isActive, page.visibility, page.defaultSize, page.defaultDisplayOption, page.mobileSize, page.background, page.wallpaper)]; case 3: response_9 = _a.sent(); throw response_9.body; @@ -9511,7 +9506,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -9521,7 +9516,7 @@ var Report = /** @class */ (function (_super) { case 2: response = _a.sent(); activePage = response.body.find(function (page) { return page.isActive; }); - return [2 /*return*/, new page_1.Page(this, activePage.name, activePage.displayName, activePage.isActive, activePage.visibility, activePage.defaultSize, activePage.defaultDisplayOption, activePage.mobileSize)]; + return [2 /*return*/, new page_1.Page(this, activePage.name, activePage.displayName, activePage.isActive, activePage.visibility, activePage.defaultSize, activePage.defaultDisplayOption, activePage.mobileSize, activePage.background, activePage.wallpaper)]; case 3: response_10 = _a.sent(); throw response_10.body; @@ -9557,7 +9552,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _a.label = 1; @@ -9592,7 +9587,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } page = { @@ -9640,7 +9635,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_c) { switch (_c.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl) && settings.customLayout != null) { + if (util_1.isRDLEmbed(this.config.embedUrl) && settings.customLayout != null) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } _c.label = 1; @@ -9674,10 +9669,10 @@ var Report = /** @class */ (function (_super) { * @hidden */ Report.prototype.validate = function (config) { - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { - return (0, powerbi_models_1.validatePaginatedReportLoad)(config); + if (util_1.isRDLEmbed(this.config.embedUrl)) { + return powerbi_models_1.validatePaginatedReportLoad(config); } - return (0, powerbi_models_1.validateReportLoad)(config); + return powerbi_models_1.validateReportLoad(config); }; /** * Handle config changes. @@ -9687,7 +9682,7 @@ var Report = /** @class */ (function (_super) { Report.prototype.configChanged = function (isBootstrap) { var config = this.config; if (this.isMobileSettings(config.settings)) { - config.embedUrl = (0, util_1.addParamToUrl)(config.embedUrl, "isMobile", "true"); + config.embedUrl = util_1.addParamToUrl(config.embedUrl, "isMobile", "true"); } // Calculate settings from HTML element attributes if available. var filterPaneEnabledAttribute = this.element.getAttribute(Report.filterPaneEnabledAttribute); @@ -9697,7 +9692,7 @@ var Report = /** @class */ (function (_super) { navContentPaneEnabled: (navContentPaneEnabledAttribute == null) ? undefined : (navContentPaneEnabledAttribute !== "false") }; // Set the settings back into the config. - this.config.settings = (0, util_1.assign)({}, elementAttrSettings, config.settings); + this.config.settings = util_1.assign({}, elementAttrSettings, config.settings); if (isBootstrap) { return; } @@ -9783,10 +9778,10 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } - return [4 /*yield*/, (0, util_1.isSavedInternal)(this.service.hpm, this.config.uniqueId, this.iframe.contentWindow)]; + return [4 /*yield*/, util_1.isSavedInternal(this.service.hpm, this.config.uniqueId, this.iframe.contentWindow)]; case 1: return [2 /*return*/, _a.sent()]; } }); @@ -9804,7 +9799,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } return [4 /*yield*/, this.applyThemeInternal(theme)]; @@ -9825,7 +9820,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } return [4 /*yield*/, this.applyThemeInternal({})]; @@ -9834,6 +9829,37 @@ var Report = /** @class */ (function (_super) { }); }); }; + /** + * get the theme of the report + * + * ```javascript + * report.getTheme(); + * ``` + */ + Report.prototype.getTheme = function () { + return __awaiter(this, void 0, void 0, function () { + var response, response_16; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + if (util_1.isRDLEmbed(this.config.embedUrl)) { + return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; + } + _a.label = 1; + case 1: + _a.trys.push([1, 3, , 4]); + return [4 /*yield*/, this.service.hpm.get("/report/theme", { uid: this.config.uniqueId }, this.iframe.contentWindow)]; + case 2: + response = _a.sent(); + return [2 /*return*/, response.body]; + case 3: + response_16 = _a.sent(); + throw response_16.body; + case 4: return [2 /*return*/]; + } + }); + }); + }; /** * Reset user's filters, slicers, and other data view changes to the default state of the report * @@ -9843,7 +9869,7 @@ var Report = /** @class */ (function (_super) { */ Report.prototype.resetPersistentFilters = function () { return __awaiter(this, void 0, void 0, function () { - var response_16; + var response_17; return __generator(this, function (_a) { switch (_a.label) { case 0: @@ -9851,8 +9877,8 @@ var Report = /** @class */ (function (_super) { return [4 /*yield*/, this.service.hpm.delete("/report/userState", null, { uid: this.config.uniqueId }, this.iframe.contentWindow)]; case 1: return [2 /*return*/, _a.sent()]; case 2: - response_16 = _a.sent(); - throw response_16.body; + response_17 = _a.sent(); + throw response_17.body; case 3: return [2 /*return*/]; } }); @@ -9867,7 +9893,7 @@ var Report = /** @class */ (function (_super) { */ Report.prototype.savePersistentFilters = function () { return __awaiter(this, void 0, void 0, function () { - var response_17; + var response_18; return __generator(this, function (_a) { switch (_a.label) { case 0: @@ -9875,8 +9901,8 @@ var Report = /** @class */ (function (_super) { return [4 /*yield*/, this.service.hpm.post("/report/userState", null, { uid: this.config.uniqueId }, this.iframe.contentWindow)]; case 1: return [2 /*return*/, _a.sent()]; case 2: - response_17 = _a.sent(); - throw response_17.body; + response_18 = _a.sent(); + throw response_18.body; case 3: return [2 /*return*/]; } }); @@ -9894,7 +9920,7 @@ var Report = /** @class */ (function (_super) { */ Report.prototype.arePersistentFiltersApplied = function () { return __awaiter(this, void 0, void 0, function () { - var response, response_18; + var response, response_19; return __generator(this, function (_a) { switch (_a.label) { case 0: @@ -9904,8 +9930,8 @@ var Report = /** @class */ (function (_super) { response = _a.sent(); return [2 /*return*/, response.body]; case 2: - response_18 = _a.sent(); - throw response_18.body; + response_19 = _a.sent(); + throw response_19.body; case 3: return [2 /*return*/]; } }); @@ -9931,7 +9957,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } commandCopy = JSON.parse(JSON.stringify(this.commands)); @@ -9982,7 +10008,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } newCommands = this.createMenuCommand("visualContextMenu", commandName, commandTitle, contextMenuTitle, menuLocation, visualName, visualType, groupName); @@ -10018,7 +10044,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } commandCopy = JSON.parse(JSON.stringify(this.commands)); @@ -10073,7 +10099,7 @@ var Report = /** @class */ (function (_super) { return __generator(this, function (_a) { switch (_a.label) { case 0: - if ((0, util_1.isRDLEmbed)(this.config.embedUrl)) { + if (util_1.isRDLEmbed(this.config.embedUrl)) { return [2 /*return*/, Promise.reject(errors_1.APINotSupportedForRDLError)]; } newCommands = this.createMenuCommand("visualOptionsMenu", commandName, commandTitle, optionsMenuTitle, menuLocation, visualName, visualType, groupName, commandIcon); @@ -10296,7 +10322,7 @@ var Report = /** @class */ (function (_super) { visualType: visualType }; } - return __spreadArray(__spreadArray([], this.commands, true), [newCommandObj], false); + return __spreadArray(__spreadArray([], this.commands), [newCommandObj]); }; /** * @hidden @@ -10347,7 +10373,7 @@ var Report = /** @class */ (function (_super) { */ Report.prototype.applyThemeInternal = function (theme) { return __awaiter(this, void 0, void 0, function () { - var response, response_19; + var response, response_20; return __generator(this, function (_a) { switch (_a.label) { case 0: @@ -10357,8 +10383,8 @@ var Report = /** @class */ (function (_super) { response = _a.sent(); return [2 /*return*/, response.body]; case 2: - response_19 = _a.sent(); - throw response_19.body; + response_20 = _a.sent(); + throw response_20.body; case 3: return [2 /*return*/]; } }); @@ -10387,11 +10413,12 @@ var Report = /** @class */ (function (_super) { }; /** * Return the current zoom level of the report. + * * @returns {Promise} */ Report.prototype.getZoom = function () { return __awaiter(this, void 0, void 0, function () { - var response, response_20; + var response, response_21; return __generator(this, function (_a) { switch (_a.label) { case 0: @@ -10401,8 +10428,8 @@ var Report = /** @class */ (function (_super) { response = _a.sent(); return [2 /*return*/, response.body]; case 2: - response_20 = _a.sent(); - throw response_20.body; + response_21 = _a.sent(); + throw response_21.body; case 3: return [2 /*return*/]; } }); @@ -10410,6 +10437,7 @@ var Report = /** @class */ (function (_super) { }; /** * Sets the report's zoom level. + * * @param zoomLevel zoom level to set */ Report.prototype.setZoom = function (zoomLevel) { @@ -11043,7 +11071,7 @@ var Tile = /** @class */ (function (_super) { */ Tile.prototype.validate = function (config) { var embedConfig = config; - return (0, powerbi_models_1.validateTileLoad)(embedConfig); + return powerbi_models_1.validateTileLoad(embedConfig); }; /** * Handle config changes. @@ -11260,7 +11288,7 @@ function generateUUID() { if (typeof performance !== 'undefined' && typeof performance.now === 'function') { d += performance.now(); } - return 'xxxxxxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + return 'xxxxxxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (_c) { // Generate a random number, scaled from 0 to 15. var r = (getRandomValue() % 16); // Shift 4 times to divide by 16 diff --git a/dist/powerbi.min.js b/dist/powerbi.min.js index c64fcd9d..50b9f657 100644 --- a/dist/powerbi.min.js +++ b/dist/powerbi.min.js @@ -1,7 +1,7 @@ -// powerbi-client v2.18.6 +// powerbi-client v2.18.7 // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports["powerbi-client"]=e():t["powerbi-client"]=e()}(this,(function(){return function(t){var e={};function r(i){if(e[i])return e[i].exports;var a=e[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,r),a.l=!0,a.exports}return r.m=t,r.c=e,r.d=function(t,e,i){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)r.d(i,a,function(e){return t[e]}.bind(null,a));return i},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=13)}([function(t,e,r){var i;i=function(){return function(t){var e={};function r(i){if(e[i])return e[i].exports;var a=e[i]={i:i,l:!1,exports:{}};return t[i].call(a.exports,a,a.exports,r),a.l=!0,a.exports}return r.m=t,r.c=e,r.d=function(t,e,i){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var a in t)r.d(i,a,function(e){return t[e]}.bind(null,a));return i},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=0)}([function(t,e,r){var i,a=this&&this.__extends||(i=function(t,e){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}i(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0}),e.validateZoomLevel=e.validateCustomTheme=e.validateCommandsSettings=e.validateVisualSettings=e.validateVisualHeader=e.validateExportDataRequest=e.validateQnaInterpretInputData=e.validateLoadQnaConfiguration=e.validateSaveAsParameters=e.validateUpdateFiltersRequest=e.validateFilter=e.validatePage=e.validateTileLoad=e.validateDashboardLoad=e.validateCreateReport=e.validatePaginatedReportLoad=e.validateReportLoad=e.validateMenuGroupExtension=e.validateExtension=e.validateCustomPageSize=e.validateVisualizationsPane=e.validateSyncSlicersPane=e.validateSelectionPane=e.validatePageNavigationPane=e.validateFieldsPane=e.validateFiltersPane=e.validateBookmarksPane=e.validatePanes=e.validateSettings=e.validateCaptureBookmarkRequest=e.validateApplyBookmarkStateRequest=e.validateApplyBookmarkByNameRequest=e.validateAddBookmarkRequest=e.validatePlayBookmarkRequest=e.validateSlicerState=e.validateSlicer=e.validateVisualSelector=e.isIExtensionArray=e.isIExtensions=e.isGroupedMenuExtension=e.isFlatMenuExtension=e.isReportFiltersArray=e.isOnLoadFilters=e.VisualDataRoleKindPreference=e.VisualDataRoleKind=e.CommandDisplayOption=e.SlicerTargetSelector=e.VisualTypeSelector=e.VisualSelector=e.PageSelector=e.Selector=e.SortDirection=e.LegendPosition=e.TextAlignment=e.CommonErrorCodes=e.BookmarksPlayMode=e.ExportDataType=e.QnaMode=e.PageNavigationPosition=e.isColumnAggr=e.isHierarchyLevelAggr=e.isHierarchyLevel=e.isColumn=e.isMeasure=e.getFilterType=e.isBasicFilterWithKeys=e.isFilterKeyColumnsTarget=e.AdvancedFilter=e.TupleFilter=e.IdentityFilter=e.BasicFilterWithKeys=e.BasicFilter=e.RelativeTimeFilter=e.RelativeDateFilter=e.TopNFilter=e.IncludeExcludeFilter=e.NotSupportedFilter=e.Filter=e.RelativeDateOperators=e.RelativeDateFilterTimeUnit=e.FilterType=e.FiltersLevel=e.FiltersOperations=e.MenuLocation=e.ContrastMode=e.TokenType=e.ViewMode=e.Permissions=e.SectionVisibility=e.ReportAlignment=e.HyperlinkClickBehavior=e.LayoutType=e.VisualContainerDisplayMode=e.BackgroundType=e.DisplayOption=e.PageSizeType=e.TraceType=void 0;var o,n=r(1);!function(t){t[t.Information=0]="Information",t[t.Verbose=1]="Verbose",t[t.Warning=2]="Warning",t[t.Error=3]="Error",t[t.ExpectedError=4]="ExpectedError",t[t.UnexpectedError=5]="UnexpectedError",t[t.Fatal=6]="Fatal"}(e.TraceType||(e.TraceType={})),function(t){t[t.Widescreen=0]="Widescreen",t[t.Standard=1]="Standard",t[t.Cortana=2]="Cortana",t[t.Letter=3]="Letter",t[t.Custom=4]="Custom",t[t.Mobile=5]="Mobile"}(e.PageSizeType||(e.PageSizeType={})),function(t){t[t.FitToPage=0]="FitToPage",t[t.FitToWidth=1]="FitToWidth",t[t.ActualSize=2]="ActualSize"}(e.DisplayOption||(e.DisplayOption={})),function(t){t[t.Default=0]="Default",t[t.Transparent=1]="Transparent"}(e.BackgroundType||(e.BackgroundType={})),function(t){t[t.Visible=0]="Visible",t[t.Hidden=1]="Hidden"}(e.VisualContainerDisplayMode||(e.VisualContainerDisplayMode={})),function(t){t[t.Master=0]="Master",t[t.Custom=1]="Custom",t[t.MobilePortrait=2]="MobilePortrait",t[t.MobileLandscape=3]="MobileLandscape"}(e.LayoutType||(e.LayoutType={})),function(t){t[t.Navigate=0]="Navigate",t[t.NavigateAndRaiseEvent=1]="NavigateAndRaiseEvent",t[t.RaiseEvent=2]="RaiseEvent"}(e.HyperlinkClickBehavior||(e.HyperlinkClickBehavior={})),function(t){t[t.Left=0]="Left",t[t.Center=1]="Center",t[t.Right=2]="Right",t[t.None=3]="None"}(e.ReportAlignment||(e.ReportAlignment={})),function(t){t[t.AlwaysVisible=0]="AlwaysVisible",t[t.HiddenInViewMode=1]="HiddenInViewMode"}(e.SectionVisibility||(e.SectionVisibility={})),function(t){t[t.Read=0]="Read",t[t.ReadWrite=1]="ReadWrite",t[t.Copy=2]="Copy",t[t.Create=4]="Create",t[t.All=7]="All"}(e.Permissions||(e.Permissions={})),function(t){t[t.View=0]="View",t[t.Edit=1]="Edit"}(e.ViewMode||(e.ViewMode={})),function(t){t[t.Aad=0]="Aad",t[t.Embed=1]="Embed"}(e.TokenType||(e.TokenType={})),function(t){t[t.None=0]="None",t[t.HighContrast1=1]="HighContrast1",t[t.HighContrast2=2]="HighContrast2",t[t.HighContrastBlack=3]="HighContrastBlack",t[t.HighContrastWhite=4]="HighContrastWhite"}(e.ContrastMode||(e.ContrastMode={})),function(t){t[t.Bottom=0]="Bottom",t[t.Top=1]="Top"}(e.MenuLocation||(e.MenuLocation={})),function(t){t[t.RemoveAll=0]="RemoveAll",t[t.ReplaceAll=1]="ReplaceAll",t[t.Add=2]="Add",t[t.Replace=3]="Replace"}(e.FiltersOperations||(e.FiltersOperations={})),function(t){t[t.Report=0]="Report",t[t.Page=1]="Page",t[t.Visual=2]="Visual"}(e.FiltersLevel||(e.FiltersLevel={})),function(t){t[t.Advanced=0]="Advanced",t[t.Basic=1]="Basic",t[t.Unknown=2]="Unknown",t[t.IncludeExclude=3]="IncludeExclude",t[t.RelativeDate=4]="RelativeDate",t[t.TopN=5]="TopN",t[t.Tuple=6]="Tuple",t[t.RelativeTime=7]="RelativeTime",t[t.Identity=8]="Identity"}(o=e.FilterType||(e.FilterType={})),function(t){t[t.Days=0]="Days",t[t.Weeks=1]="Weeks",t[t.CalendarWeeks=2]="CalendarWeeks",t[t.Months=3]="Months",t[t.CalendarMonths=4]="CalendarMonths",t[t.Years=5]="Years",t[t.CalendarYears=6]="CalendarYears",t[t.Minutes=7]="Minutes",t[t.Hours=8]="Hours"}(e.RelativeDateFilterTimeUnit||(e.RelativeDateFilterTimeUnit={})),function(t){t[t.InLast=0]="InLast",t[t.InThis=1]="InThis",t[t.InNext=2]="InNext"}(e.RelativeDateOperators||(e.RelativeDateOperators={}));var l=function(){function t(t,e){this.target=t,this.filterType=e}return t.prototype.toJSON=function(){var t={$schema:this.schemaUrl,target:this.target,filterType:this.filterType};return void 0!==this.displaySettings&&(t.displaySettings=this.displaySettings),t},t}();e.Filter=l;var s=function(t){function e(r,i,a){var n=t.call(this,r,o.Unknown)||this;return n.message=i,n.notSupportedTypeName=a,n.schemaUrl=e.schemaUrl,n}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.message=this.message,e.notSupportedTypeName=this.notSupportedTypeName,e},e.schemaUrl="http://powerbi.com/product/schema#notSupported",e}(l);e.NotSupportedFilter=s;var u=function(t){function e(r,i,a){var n=t.call(this,r,o.IncludeExclude)||this;return n.values=a,n.isExclude=i,n.schemaUrl=e.schemaUrl,n}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.isExclude=this.isExclude,e.values=this.values,e},e.schemaUrl="http://powerbi.com/product/schema#includeExclude",e}(l);e.IncludeExcludeFilter=u;var d=function(t){function e(r,i,a,n){var l=t.call(this,r,o.TopN)||this;return l.operator=i,l.itemCount=a,l.schemaUrl=e.schemaUrl,l.orderBy=n,l}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.operator=this.operator,e.itemCount=this.itemCount,e.orderBy=this.orderBy,e},e.schemaUrl="http://powerbi.com/product/schema#topN",e}(l);e.TopNFilter=d;var c=function(t){function e(r,i,a,n,l){var s=t.call(this,r,o.RelativeDate)||this;return s.operator=i,s.timeUnitsCount=a,s.timeUnitType=n,s.includeToday=l,s.schemaUrl=e.schemaUrl,s}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.operator=this.operator,e.timeUnitsCount=this.timeUnitsCount,e.timeUnitType=this.timeUnitType,e.includeToday=this.includeToday,e},e.schemaUrl="http://powerbi.com/product/schema#relativeDate",e}(l);e.RelativeDateFilter=c;var p=function(t){function e(r,i,a,n){var l=t.call(this,r,o.RelativeTime)||this;return l.operator=i,l.timeUnitsCount=a,l.timeUnitType=n,l.schemaUrl=e.schemaUrl,l}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.operator=this.operator,e.timeUnitsCount=this.timeUnitsCount,e.timeUnitType=this.timeUnitType,e},e.schemaUrl="http://powerbi.com/product/schema#relativeTime",e}(l);e.RelativeTimeFilter=p;var f=function(t){function e(r,i){for(var a=[],n=2;n0&&!a)throw new Error("You should pass the values to be filtered for each key. You passed: no values and "+n+" keys");if(0===n&&a&&a.length>0)throw new Error("You passed key values but your target object doesn't contain the keys to be filtered");for(var l=0,s=o.keyValues;l2)throw new Error("AdvancedFilters may not have more than two conditions. You passed: "+a.length);if(1===l.length&&"And"!==i)throw new Error('Logical Operator must be "And" when there is only one condition provided');return s.conditions=l,s}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.logicalOperator=this.logicalOperator,e.conditions=this.conditions,e},e.schemaUrl="http://powerbi.com/product/schema#advanced",e}(l);function V(t){if(t.filterType)return t.filterType;var e=t,r=t;return"string"==typeof e.operator&&Array.isArray(e.values)?o.Basic:"string"==typeof r.logicalOperator&&Array.isArray(r.conditions)?o.Advanced:o.Unknown}function g(t){return!(!t.table||!t.column||t.aggregationFunction)}e.AdvancedFilter=m,e.isFilterKeyColumnsTarget=function(t){return g(t)&&!!t.keys},e.isBasicFilterWithKeys=function(t){return V(t)===o.Basic&&!!t.keyValues},e.getFilterType=V,e.isMeasure=function(t){return void 0!==t.table&&void 0!==t.measure},e.isColumn=g,e.isHierarchyLevel=function(t){return!(!(t.table&&t.hierarchy&&t.hierarchyLevel)||t.aggregationFunction)},e.isHierarchyLevelAggr=function(t){return!!(t.table&&t.hierarchy&&t.hierarchyLevel&&t.aggregationFunction)},e.isColumnAggr=function(t){return!!(t.table&&t.column&&t.aggregationFunction)},function(t){t[t.Bottom=0]="Bottom",t[t.Left=1]="Left"}(e.PageNavigationPosition||(e.PageNavigationPosition={})),function(t){t[t.Interactive=0]="Interactive",t[t.ResultOnly=1]="ResultOnly"}(e.QnaMode||(e.QnaMode={})),function(t){t[t.Summarized=0]="Summarized",t[t.Underlying=1]="Underlying"}(e.ExportDataType||(e.ExportDataType={})),function(t){t[t.Off=0]="Off",t[t.Presentation=1]="Presentation"}(e.BookmarksPlayMode||(e.BookmarksPlayMode={})),e.CommonErrorCodes={TokenExpired:"TokenExpired",NotFound:"PowerBIEntityNotFound",InvalidParameters:"Invalid parameters",LoadReportFailed:"LoadReportFailed",NotAuthorized:"PowerBINotAuthorizedException",FailedToLoadModel:"ExplorationContainer_FailedToLoadModel_DefaultDetails"},e.TextAlignment={Left:"left",Center:"center",Right:"right"},e.LegendPosition={Top:"Top",Bottom:"Bottom",Right:"Right",Left:"Left",TopCenter:"TopCenter",BottomCenter:"BottomCenter",RightCenter:"RightCenter",LeftCenter:"LeftCenter"},function(t){t[t.Ascending=1]="Ascending",t[t.Descending=2]="Descending"}(e.SortDirection||(e.SortDirection={}));var b=function(){function t(t){this.$schema=t}return t.prototype.toJSON=function(){return{$schema:this.$schema}},t}();e.Selector=b;var w=function(t){function e(r){var i=t.call(this,e.schemaUrl)||this;return i.pageName=r,i}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.pageName=this.pageName,e},e.schemaUrl="http://powerbi.com/product/schema#pageSelector",e}(b);e.PageSelector=w;var P=function(t){function e(r){var i=t.call(this,e.schemaUrl)||this;return i.visualName=r,i}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.visualName=this.visualName,e},e.schemaUrl="http://powerbi.com/product/schema#visualSelector",e}(b);e.VisualSelector=P;var _=function(t){function e(e){var r=t.call(this,P.schemaUrl)||this;return r.visualType=e,r}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.visualType=this.visualType,e},e.schemaUrl="http://powerbi.com/product/schema#visualTypeSelector",e}(b);e.VisualTypeSelector=_;var O=function(t){function e(e){var r=t.call(this,P.schemaUrl)||this;return r.target=e,r}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.target=this.target,e},e.schemaUrl="http://powerbi.com/product/schema#slicerTargetSelector",e}(b);function S(t){return Array.isArray(t)}function T(t){return t&&!!t.groupName}function E(t){return Array.isArray(t)}function F(t){var e=t.message;return e||(e=t.path+" is invalid. Not meeting "+t.keyword+" constraint"),{message:e}}e.SlicerTargetSelector=O,function(t){t[t.Enabled=0]="Enabled",t[t.Disabled=1]="Disabled",t[t.Hidden=2]="Hidden"}(e.CommandDisplayOption||(e.CommandDisplayOption={})),function(t){t[t.Grouping=0]="Grouping",t[t.Measure=1]="Measure",t[t.GroupingOrMeasure=2]="GroupingOrMeasure"}(e.VisualDataRoleKind||(e.VisualDataRoleKind={})),function(t){t[t.Measure=0]="Measure",t[t.Grouping=1]="Grouping"}(e.VisualDataRoleKindPreference||(e.VisualDataRoleKindPreference={})),e.isOnLoadFilters=function(t){return t&&!S(t)},e.isReportFiltersArray=S,e.isFlatMenuExtension=function(t){return t&&!T(t)},e.isGroupedMenuExtension=T,e.isIExtensions=function(t){return t&&!E(t)},e.isIExtensionArray=E,e.validateVisualSelector=function(t){var e=n.Validators.visualSelectorValidator.validate(t);return e?e.map(F):void 0},e.validateSlicer=function(t){var e=n.Validators.slicerValidator.validate(t);return e?e.map(F):void 0},e.validateSlicerState=function(t){var e=n.Validators.slicerStateValidator.validate(t);return e?e.map(F):void 0},e.validatePlayBookmarkRequest=function(t){var e=n.Validators.playBookmarkRequestValidator.validate(t);return e?e.map(F):void 0},e.validateAddBookmarkRequest=function(t){var e=n.Validators.addBookmarkRequestValidator.validate(t);return e?e.map(F):void 0},e.validateApplyBookmarkByNameRequest=function(t){var e=n.Validators.applyBookmarkByNameRequestValidator.validate(t);return e?e.map(F):void 0},e.validateApplyBookmarkStateRequest=function(t){var e=n.Validators.applyBookmarkStateRequestValidator.validate(t);return e?e.map(F):void 0},e.validateCaptureBookmarkRequest=function(t){var e=n.Validators.captureBookmarkRequestValidator.validate(t);return e?e.map(F):void 0},e.validateSettings=function(t){var e=n.Validators.settingsValidator.validate(t);return e?e.map(F):void 0},e.validatePanes=function(t){var e=n.Validators.reportPanesValidator.validate(t);return e?e.map(F):void 0},e.validateBookmarksPane=function(t){var e=n.Validators.bookmarksPaneValidator.validate(t);return e?e.map(F):void 0},e.validateFiltersPane=function(t){var e=n.Validators.filtersPaneValidator.validate(t);return e?e.map(F):void 0},e.validateFieldsPane=function(t){var e=n.Validators.fieldsPaneValidator.validate(t);return e?e.map(F):void 0},e.validatePageNavigationPane=function(t){var e=n.Validators.pageNavigationPaneValidator.validate(t);return e?e.map(F):void 0},e.validateSelectionPane=function(t){var e=n.Validators.selectionPaneValidator.validate(t);return e?e.map(F):void 0},e.validateSyncSlicersPane=function(t){var e=n.Validators.syncSlicersPaneValidator.validate(t);return e?e.map(F):void 0},e.validateVisualizationsPane=function(t){var e=n.Validators.visualizationsPaneValidator.validate(t);return e?e.map(F):void 0},e.validateCustomPageSize=function(t){var e=n.Validators.customPageSizeValidator.validate(t);return e?e.map(F):void 0},e.validateExtension=function(t){var e=n.Validators.extensionValidator.validate(t);return e?e.map(F):void 0},e.validateMenuGroupExtension=function(t){var e=n.Validators.menuGroupExtensionValidator.validate(t);return e?e.map(F):void 0},e.validateReportLoad=function(t){var e=n.Validators.reportLoadValidator.validate(t);return e?e.map(F):void 0},e.validatePaginatedReportLoad=function(t){var e=n.Validators.paginatedReportLoadValidator.validate(t);return e?e.map(F):void 0},e.validateCreateReport=function(t){var e=n.Validators.reportCreateValidator.validate(t);return e?e.map(F):void 0},e.validateDashboardLoad=function(t){var e=n.Validators.dashboardLoadValidator.validate(t);return e?e.map(F):void 0},e.validateTileLoad=function(t){var e=n.Validators.tileLoadValidator.validate(t);return e?e.map(F):void 0},e.validatePage=function(t){var e=n.Validators.pageValidator.validate(t);return e?e.map(F):void 0},e.validateFilter=function(t){var e=n.Validators.filterValidator.validate(t);return e?e.map(F):void 0},e.validateUpdateFiltersRequest=function(t){var e=n.Validators.updateFiltersRequestValidator.validate(t);return e?e.map(F):void 0},e.validateSaveAsParameters=function(t){var e=n.Validators.saveAsParametersValidator.validate(t);return e?e.map(F):void 0},e.validateLoadQnaConfiguration=function(t){var e=n.Validators.loadQnaValidator.validate(t);return e?e.map(F):void 0},e.validateQnaInterpretInputData=function(t){var e=n.Validators.qnaInterpretInputDataValidator.validate(t);return e?e.map(F):void 0},e.validateExportDataRequest=function(t){var e=n.Validators.exportDataRequestValidator.validate(t);return e?e.map(F):void 0},e.validateVisualHeader=function(t){var e=n.Validators.visualHeaderValidator.validate(t);return e?e.map(F):void 0},e.validateVisualSettings=function(t){var e=n.Validators.visualSettingsValidator.validate(t);return e?e.map(F):void 0},e.validateCommandsSettings=function(t){var e=n.Validators.commandsSettingsValidator.validate(t);return e?e.map(F):void 0},e.validateCustomTheme=function(t){var e=n.Validators.customThemeValidator.validate(t);return e?e.map(F):void 0},e.validateZoomLevel=function(t){var e=n.Validators.zoomLevelValidator.validate(t);return e?e.map(F):void 0}},function(t,e,r){Object.defineProperty(e,"__esModule",{value:!0}),e.Validators=void 0;var i=r(2),a=r(5),o=r(6),n=r(7),l=r(8),s=r(9),u=r(10),d=r(11),c=r(12),p=r(13),f=r(14),h=r(15),v=r(16),y=r(17),m=r(18),V=r(19),g=r(20),b=r(21),w=r(22),P=r(23),_=r(24),O=r(25),S=r(26),T=r(27),E=r(28),F=r(29),R=r(4),x=r(30);e.Validators={addBookmarkRequestValidator:new a.AddBookmarkRequestValidator,advancedFilterTypeValidator:new R.EnumValidator([0]),advancedFilterValidator:new c.AdvancedFilterValidator,anyArrayValidator:new R.ArrayValidator([new S.AnyOfValidator([new R.StringValidator,new R.NumberValidator,new R.BooleanValidator])]),anyFilterValidator:new S.AnyOfValidator([new c.BasicFilterValidator,new c.AdvancedFilterValidator,new c.IncludeExcludeFilterValidator,new c.NotSupportedFilterValidator,new c.RelativeDateFilterValidator,new c.TopNFilterValidator,new c.RelativeTimeFilterValidator]),anyValueValidator:new S.AnyOfValidator([new R.StringValidator,new R.NumberValidator,new R.BooleanValidator]),actionBarValidator:new i.ActionBarValidator,applyBookmarkByNameRequestValidator:new a.ApplyBookmarkByNameRequestValidator,applyBookmarkStateRequestValidator:new a.ApplyBookmarkStateRequestValidator,applyBookmarkValidator:new S.AnyOfValidator([new a.ApplyBookmarkByNameRequestValidator,new a.ApplyBookmarkStateRequestValidator]),backgroundValidator:new R.EnumValidator([0,1]),basicFilterTypeValidator:new R.EnumValidator([1]),basicFilterValidator:new c.BasicFilterValidator,booleanArrayValidator:new R.BooleanArrayValidator,booleanValidator:new R.BooleanValidator,bookmarksPaneValidator:new h.BookmarksPaneValidator,captureBookmarkOptionsValidator:new a.CaptureBookmarkOptionsValidator,captureBookmarkRequestValidator:new a.CaptureBookmarkRequestValidator,commandDisplayOptionValidator:new R.EnumValidator([0,1,2]),commandExtensionSelectorValidator:new S.AnyOfValidator([new b.VisualSelectorValidator,new b.VisualTypeSelectorValidator]),commandExtensionArrayValidator:new R.ArrayValidator([new d.CommandExtensionValidator]),commandExtensionValidator:new d.CommandExtensionValidator,commandsSettingsArrayValidator:new R.ArrayValidator([new o.CommandsSettingsValidator]),commandsSettingsValidator:new o.CommandsSettingsValidator,conditionItemValidator:new c.ConditionItemValidator,contrastModeValidator:new R.EnumValidator([0,1,2,3,4]),customLayoutDisplayOptionValidator:new R.EnumValidator([0,1,2]),customLayoutValidator:new p.CustomLayoutValidator,customPageSizeValidator:new f.CustomPageSizeValidator,customThemeValidator:new n.CustomThemeValidator,dashboardLoadValidator:new l.DashboardLoadValidator,datasetBindingValidator:new s.DatasetBindingValidator,displayStateModeValidator:new R.EnumValidator([0,1]),displayStateValidator:new p.DisplayStateValidator,exportDataRequestValidator:new u.ExportDataRequestValidator,extensionArrayValidator:new R.ArrayValidator([new d.ExtensionValidator]),extensionsValidator:new S.AnyOfValidator([new R.ArrayValidator([new d.ExtensionValidator]),new d.ExtensionsValidator]),extensionPointsValidator:new d.ExtensionPointsValidator,extensionValidator:new d.ExtensionValidator,fieldForbiddenValidator:new T.FieldForbiddenValidator,fieldRequiredValidator:new E.FieldRequiredValidator,fieldsPaneValidator:new h.FieldsPaneValidator,filterColumnTargetValidator:new c.FilterColumnTargetValidator,filterDisplaySettingsValidator:new c.FilterDisplaySettingsValidator,filterConditionsValidator:new R.ArrayValidator([new c.ConditionItemValidator]),filterHierarchyTargetValidator:new c.FilterHierarchyTargetValidator,filterMeasureTargetValidator:new c.FilterMeasureTargetValidator,filterTargetValidator:new S.AnyOfValidator([new c.FilterColumnTargetValidator,new c.FilterHierarchyTargetValidator,new c.FilterMeasureTargetValidator]),filterValidator:new c.FilterValidator,filterTypeValidator:new R.EnumValidator([0,1,2,3,4,5,6,7]),filtersArrayValidator:new R.ArrayValidator([new c.FilterValidator]),filtersOperationsUpdateValidator:new R.EnumValidator([1,2,3]),filtersOperationsRemoveAllValidator:new R.EnumValidator([0]),filtersPaneValidator:new h.FiltersPaneValidator,hyperlinkClickBehaviorValidator:new R.EnumValidator([0,1,2]),includeExcludeFilterValidator:new c.IncludeExcludeFilterValidator,includeExludeFilterTypeValidator:new R.EnumValidator([3]),layoutTypeValidator:new R.EnumValidator([0,1,2,3]),loadQnaValidator:new v.LoadQnaValidator,menuExtensionValidator:new S.AnyOfValidator([new d.FlatMenuExtensionValidator,new d.GroupedMenuExtensionValidator]),menuGroupExtensionArrayValidator:new R.ArrayValidator([new d.MenuGroupExtensionValidator]),menuGroupExtensionValidator:new d.MenuGroupExtensionValidator,menuLocationValidator:new R.EnumValidator([0,1]),notSupportedFilterTypeValidator:new R.EnumValidator([2]),notSupportedFilterValidator:new c.NotSupportedFilterValidator,numberArrayValidator:new R.NumberArrayValidator,numberValidator:new R.NumberValidator,onLoadFiltersBaseValidator:new S.AnyOfValidator([new c.OnLoadFiltersBaseValidator,new c.OnLoadFiltersBaseRemoveOperationValidator]),pageLayoutValidator:new F.MapValidator([new R.StringValidator],[new p.VisualLayoutValidator]),pageNavigationPaneValidator:new h.PageNavigationPaneValidator,pageNavigationPositionValidator:new R.EnumValidator([0,1]),pageSizeTypeValidator:new R.EnumValidator([0,1,2,3,4,5]),pageSizeValidator:new f.PageSizeValidator,pageValidator:new f.PageValidator,pageViewFieldValidator:new f.PageViewFieldValidator,pagesLayoutValidator:new F.MapValidator([new R.StringValidator],[new p.PageLayoutValidator]),paginatedReportCommandsValidator:new o.PaginatedReportCommandsValidator,paginatedReportLoadValidator:new V.PaginatedReportLoadValidator,paginatedReportsettingsValidator:new w.PaginatedReportSettingsValidator,parametersPanelValidator:new x.ParametersPanelValidator,permissionsValidator:new R.EnumValidator([0,1,2,4,7]),playBookmarkRequestValidator:new a.PlayBookmarkRequestValidator,qnaInterpretInputDataValidator:new v.QnaInterpretInputDataValidator,qnaPanesValidator:new h.QnaPanesValidator,qnaSettingValidator:new v.QnaSettingsValidator,relativeDateFilterOperatorValidator:new R.EnumValidator([0,1,2]),relativeDateFilterTimeUnitTypeValidator:new R.EnumValidator([0,1,2,3,4,5,6]),relativeDateFilterTypeValidator:new R.EnumValidator([4]),relativeDateFilterValidator:new c.RelativeDateFilterValidator,relativeDateTimeFilterTypeValidator:new R.EnumValidator([4,7]),relativeDateTimeFilterUnitTypeValidator:new R.EnumValidator([0,1,2,3,4,5,6,7,8]),relativeTimeFilterTimeUnitTypeValidator:new R.EnumValidator([7,8]),relativeTimeFilterTypeValidator:new R.EnumValidator([7]),relativeTimeFilterValidator:new c.RelativeTimeFilterValidator,reportBarsValidator:new i.ReportBarsValidator,reportCreateValidator:new y.ReportCreateValidator,reportLoadFiltersValidator:new S.AnyOfValidator([new R.ArrayValidator([new c.FilterValidator]),new c.OnLoadFiltersValidator]),reportLoadValidator:new m.ReportLoadValidator,reportPanesValidator:new h.ReportPanesValidator,saveAsParametersValidator:new g.SaveAsParametersValidator,selectionPaneValidator:new h.SelectionPaneValidator,settingsValidator:new w.SettingsValidator,singleCommandSettingsValidator:new o.SingleCommandSettingsValidator,slicerSelectorValidator:new S.AnyOfValidator([new b.VisualSelectorValidator,new b.SlicerTargetSelectorValidator]),slicerStateValidator:new P.SlicerStateValidator,slicerTargetValidator:new S.AnyOfValidator([new c.FilterColumnTargetValidator,new c.FilterHierarchyTargetValidator,new c.FilterMeasureTargetValidator,new c.FilterKeyColumnsTargetValidator,new c.FilterKeyHierarchyTargetValidator]),slicerValidator:new P.SlicerValidator,stringArrayValidator:new R.StringArrayValidator,stringValidator:new R.StringValidator,syncSlicersPaneValidator:new h.SyncSlicersPaneValidator,tileLoadValidator:new _.TileLoadValidator,tokenTypeValidator:new R.EnumValidator([0,1]),topNFilterTypeValidator:new R.EnumValidator([5]),topNFilterValidator:new c.TopNFilterValidator,updateFiltersRequestValidator:new S.AnyOfValidator([new c.UpdateFiltersRequestValidator,new c.RemoveFiltersRequestValidator]),viewModeValidator:new R.EnumValidator([0,1]),visualCommandSelectorValidator:new S.AnyOfValidator([new b.VisualSelectorValidator,new b.VisualTypeSelectorValidator]),visualHeaderSelectorValidator:new S.AnyOfValidator([new b.VisualSelectorValidator,new b.VisualTypeSelectorValidator]),visualHeaderSettingsValidator:new O.VisualHeaderSettingsValidator,visualHeaderValidator:new O.VisualHeaderValidator,visualHeadersValidator:new R.ArrayValidator([new O.VisualHeaderValidator]),visualizationsPaneValidator:new h.VisualizationsPaneValidator,visualLayoutValidator:new p.VisualLayoutValidator,visualSelectorValidator:new b.VisualSelectorValidator,visualSettingsValidator:new O.VisualSettingsValidator,visualTypeSelectorValidator:new b.VisualTypeSelectorValidator,zoomLevelValidator:new R.RangeValidator(.25,4)}},function(t,e,r){var i,a=this&&this.__extends||(i=function(t,e){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}i(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0}),e.ActionBarValidator=e.ReportBarsValidator=void 0;var o=r(3),n=r(4),l=r(1),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return a(e,t),e.prototype.validate=function(e,r,i){if(null==e)return null;var a=t.prototype.validate.call(this,e,r,i);if(a)return a;var n=[{field:"actionBar",validators:[l.Validators.actionBarValidator]}];return new o.MultipleFieldsValidator(n).validate(e,r,i)},e}(n.ObjectValidator);e.ReportBarsValidator=s;var u=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return a(e,t),e.prototype.validate=function(e,r,i){if(null==e)return null;var a=t.prototype.validate.call(this,e,r,i);if(a)return a;var n=[{field:"visible",validators:[l.Validators.booleanValidator]}];return new o.MultipleFieldsValidator(n).validate(e,r,i)},e}(n.ObjectValidator);e.ActionBarValidator=u},function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.MultipleFieldsValidator=void 0;var r=function(){function t(t){this.fieldValidatorsPairs=t}return t.prototype.validate=function(t,e,r){if(!this.fieldValidatorsPairs)return null;for(var i=e?e+"."+r:r,a=0,o=this.fieldValidatorsPairs;athis.maxValue||e0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0?"&":"?";return t+=i+e+"="+r},e.isSavedInternal=function(t,e,a){return r(this,void 0,void 0,(function(){return i(this,(function(r){switch(r.label){case 0:return r.trys.push([0,2,,3]),[4,t.get("/report/hasUnsavedChanges",{uid:e},a)];case 1:return[2,!r.sent().body];case 2:throw r.sent().body;case 3:return[2]}}))}))},e.isRDLEmbed=function(t){return t&&t.toLowerCase().indexOf("/rdlembed?")>=0},e.autoAuthInEmbedUrl=function(t){return t&&decodeURIComponent(t).toLowerCase().indexOf("autoauth=true")>=0},e.getRandomValue=o,e.getTimeDiffInMilliseconds=function(t,e){return Math.abs(t.getTime()-e.getTime())}},function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.FilterBuilder=void 0;var r=function(){function t(){}return t.prototype.withTargetObject=function(t){return this.target=t,this},t.prototype.withColumnTarget=function(t,e){return this.target={table:t,column:e},this},t.prototype.withMeasureTarget=function(t,e){return this.target={table:t,measure:e},this},t.prototype.withHierarchyLevelTarget=function(t,e,r){return this.target={table:t,hierarchy:e,hierarchyLevel:r},this},t.prototype.withColumnAggregation=function(t,e,r){return this.target={table:t,column:e,aggregationFunction:r},this},t.prototype.withHierarchyLevelAggregationTarget=function(t,e,r,i){return this.target={table:t,hierarchy:e,hierarchyLevel:r,aggregationFunction:i},this},t}();e.FilterBuilder=r},function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.EmbedUrlNotSupported=e.APINotSupportedForRDLError=void 0,e.APINotSupportedForRDLError="This API is currently not supported for RDL reports",e.EmbedUrlNotSupported="Embed URL is invalid for this scenario. Please use Power BI REST APIs to get the valid URL"},function(t,e,r){var i,a=this&&this.__extends||(i=function(t,e){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r])})(t,e)},function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function r(){this.constructor=t}i(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__awaiter||function(t,e,r,i){return new(r||(r=Promise))((function(a,o){function n(t){try{s(i.next(t))}catch(t){o(t)}}function l(t){try{s(i.throw(t))}catch(t){o(t)}}function s(t){var e;t.done?a(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(n,l)}s((i=i.apply(t,e||[])).next())}))},n=this&&this.__generator||function(t,e){var r,i,a,o,n={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]};return o={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function l(o){return function(l){return function(o){if(r)throw new TypeError("Generator is already executing.");for(;n;)try{if(r=1,i&&(a=2&o[0]?i.return:o[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,o[1])).done)return a;switch(i=0,a&&(o=[2&o[0],a.value]),o[0]){case 0:case 1:a=o;break;case 4:return n.label++,{value:o[1],done:!1};case 5:n.label++,i=o[1],o=[0];continue;case 7:o=n.ops.pop(),n.trys.pop();continue;default:if(!(a=n.trys,(a=a.length>0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&!a)throw new Error("You should pass the values to be filtered for each key. You passed: no values and "+n+" keys");if(0===n&&a&&a.length>0)throw new Error("You passed key values but your target object doesn't contain the keys to be filtered");for(var l=0,s=o.keyValues;l2)throw new Error("AdvancedFilters may not have more than two conditions. You passed: "+a.length);if(1===l.length&&"And"!==i)throw new Error('Logical Operator must be "And" when there is only one condition provided');return s.conditions=l,s}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.logicalOperator=this.logicalOperator,e.conditions=this.conditions,e},e.schemaUrl="http://powerbi.com/product/schema#advanced",e}(l);function V(t){if(t.filterType)return t.filterType;var e=t,r=t;return"string"==typeof e.operator&&Array.isArray(e.values)?o.Basic:"string"==typeof r.logicalOperator&&Array.isArray(r.conditions)?o.Advanced:o.Unknown}function g(t){return!(!t.table||!t.column||t.aggregationFunction)}e.AdvancedFilter=m,e.isFilterKeyColumnsTarget=function(t){return g(t)&&!!t.keys},e.isBasicFilterWithKeys=function(t){return V(t)===o.Basic&&!!t.keyValues},e.getFilterType=V,e.isMeasure=function(t){return void 0!==t.table&&void 0!==t.measure},e.isColumn=g,e.isHierarchyLevel=function(t){return!(!(t.table&&t.hierarchy&&t.hierarchyLevel)||t.aggregationFunction)},e.isHierarchyLevelAggr=function(t){return!!(t.table&&t.hierarchy&&t.hierarchyLevel&&t.aggregationFunction)},e.isColumnAggr=function(t){return!!(t.table&&t.column&&t.aggregationFunction)},function(t){t[t.Bottom=0]="Bottom",t[t.Left=1]="Left"}(e.PageNavigationPosition||(e.PageNavigationPosition={})),function(t){t[t.Interactive=0]="Interactive",t[t.ResultOnly=1]="ResultOnly"}(e.QnaMode||(e.QnaMode={})),function(t){t[t.Summarized=0]="Summarized",t[t.Underlying=1]="Underlying"}(e.ExportDataType||(e.ExportDataType={})),function(t){t[t.Off=0]="Off",t[t.Presentation=1]="Presentation"}(e.BookmarksPlayMode||(e.BookmarksPlayMode={})),e.CommonErrorCodes={TokenExpired:"TokenExpired",NotFound:"PowerBIEntityNotFound",InvalidParameters:"Invalid parameters",LoadReportFailed:"LoadReportFailed",NotAuthorized:"PowerBINotAuthorizedException",FailedToLoadModel:"ExplorationContainer_FailedToLoadModel_DefaultDetails"},e.TextAlignment={Left:"left",Center:"center",Right:"right"},e.LegendPosition={Top:"Top",Bottom:"Bottom",Right:"Right",Left:"Left",TopCenter:"TopCenter",BottomCenter:"BottomCenter",RightCenter:"RightCenter",LeftCenter:"LeftCenter"},function(t){t[t.Ascending=1]="Ascending",t[t.Descending=2]="Descending"}(e.SortDirection||(e.SortDirection={}));var b=function(){function t(t){this.$schema=t}return t.prototype.toJSON=function(){return{$schema:this.$schema}},t}();e.Selector=b;var w=function(t){function e(r){var i=t.call(this,e.schemaUrl)||this;return i.pageName=r,i}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.pageName=this.pageName,e},e.schemaUrl="http://powerbi.com/product/schema#pageSelector",e}(b);e.PageSelector=w;var P=function(t){function e(r){var i=t.call(this,e.schemaUrl)||this;return i.visualName=r,i}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.visualName=this.visualName,e},e.schemaUrl="http://powerbi.com/product/schema#visualSelector",e}(b);e.VisualSelector=P;var _=function(t){function e(e){var r=t.call(this,P.schemaUrl)||this;return r.visualType=e,r}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.visualType=this.visualType,e},e.schemaUrl="http://powerbi.com/product/schema#visualTypeSelector",e}(b);e.VisualTypeSelector=_;var O=function(t){function e(e){var r=t.call(this,P.schemaUrl)||this;return r.target=e,r}return a(e,t),e.prototype.toJSON=function(){var e=t.prototype.toJSON.call(this);return e.target=this.target,e},e.schemaUrl="http://powerbi.com/product/schema#slicerTargetSelector",e}(b);function S(t){return Array.isArray(t)}function T(t){return t&&!!t.groupName}function E(t){return Array.isArray(t)}function F(t){var e=t.message;return e||(e=t.path+" is invalid. Not meeting "+t.keyword+" constraint"),{message:e}}e.SlicerTargetSelector=O,function(t){t[t.Enabled=0]="Enabled",t[t.Disabled=1]="Disabled",t[t.Hidden=2]="Hidden"}(e.CommandDisplayOption||(e.CommandDisplayOption={})),function(t){t[t.Grouping=0]="Grouping",t[t.Measure=1]="Measure",t[t.GroupingOrMeasure=2]="GroupingOrMeasure"}(e.VisualDataRoleKind||(e.VisualDataRoleKind={})),function(t){t[t.Measure=0]="Measure",t[t.Grouping=1]="Grouping"}(e.VisualDataRoleKindPreference||(e.VisualDataRoleKindPreference={})),e.isOnLoadFilters=function(t){return t&&!S(t)},e.isReportFiltersArray=S,e.isFlatMenuExtension=function(t){return t&&!T(t)},e.isGroupedMenuExtension=T,e.isIExtensions=function(t){return t&&!E(t)},e.isIExtensionArray=E,e.validateVisualSelector=function(t){var e=n.Validators.visualSelectorValidator.validate(t);return e?e.map(F):void 0},e.validateSlicer=function(t){var e=n.Validators.slicerValidator.validate(t);return e?e.map(F):void 0},e.validateSlicerState=function(t){var e=n.Validators.slicerStateValidator.validate(t);return e?e.map(F):void 0},e.validatePlayBookmarkRequest=function(t){var e=n.Validators.playBookmarkRequestValidator.validate(t);return e?e.map(F):void 0},e.validateAddBookmarkRequest=function(t){var e=n.Validators.addBookmarkRequestValidator.validate(t);return e?e.map(F):void 0},e.validateApplyBookmarkByNameRequest=function(t){var e=n.Validators.applyBookmarkByNameRequestValidator.validate(t);return e?e.map(F):void 0},e.validateApplyBookmarkStateRequest=function(t){var e=n.Validators.applyBookmarkStateRequestValidator.validate(t);return e?e.map(F):void 0},e.validateCaptureBookmarkRequest=function(t){var e=n.Validators.captureBookmarkRequestValidator.validate(t);return e?e.map(F):void 0},e.validateSettings=function(t){var e=n.Validators.settingsValidator.validate(t);return e?e.map(F):void 0},e.validatePanes=function(t){var e=n.Validators.reportPanesValidator.validate(t);return e?e.map(F):void 0},e.validateBookmarksPane=function(t){var e=n.Validators.bookmarksPaneValidator.validate(t);return e?e.map(F):void 0},e.validateFiltersPane=function(t){var e=n.Validators.filtersPaneValidator.validate(t);return e?e.map(F):void 0},e.validateFieldsPane=function(t){var e=n.Validators.fieldsPaneValidator.validate(t);return e?e.map(F):void 0},e.validatePageNavigationPane=function(t){var e=n.Validators.pageNavigationPaneValidator.validate(t);return e?e.map(F):void 0},e.validateSelectionPane=function(t){var e=n.Validators.selectionPaneValidator.validate(t);return e?e.map(F):void 0},e.validateSyncSlicersPane=function(t){var e=n.Validators.syncSlicersPaneValidator.validate(t);return e?e.map(F):void 0},e.validateVisualizationsPane=function(t){var e=n.Validators.visualizationsPaneValidator.validate(t);return e?e.map(F):void 0},e.validateCustomPageSize=function(t){var e=n.Validators.customPageSizeValidator.validate(t);return e?e.map(F):void 0},e.validateExtension=function(t){var e=n.Validators.extensionValidator.validate(t);return e?e.map(F):void 0},e.validateMenuGroupExtension=function(t){var e=n.Validators.menuGroupExtensionValidator.validate(t);return e?e.map(F):void 0},e.validateReportLoad=function(t){var e=n.Validators.reportLoadValidator.validate(t);return e?e.map(F):void 0},e.validatePaginatedReportLoad=function(t){var e=n.Validators.paginatedReportLoadValidator.validate(t);return e?e.map(F):void 0},e.validateCreateReport=function(t){var e=n.Validators.reportCreateValidator.validate(t);return e?e.map(F):void 0},e.validateDashboardLoad=function(t){var e=n.Validators.dashboardLoadValidator.validate(t);return e?e.map(F):void 0},e.validateTileLoad=function(t){var e=n.Validators.tileLoadValidator.validate(t);return e?e.map(F):void 0},e.validatePage=function(t){var e=n.Validators.pageValidator.validate(t);return e?e.map(F):void 0},e.validateFilter=function(t){var e=n.Validators.filterValidator.validate(t);return e?e.map(F):void 0},e.validateUpdateFiltersRequest=function(t){var e=n.Validators.updateFiltersRequestValidator.validate(t);return e?e.map(F):void 0},e.validateSaveAsParameters=function(t){var e=n.Validators.saveAsParametersValidator.validate(t);return e?e.map(F):void 0},e.validateLoadQnaConfiguration=function(t){var e=n.Validators.loadQnaValidator.validate(t);return e?e.map(F):void 0},e.validateQnaInterpretInputData=function(t){var e=n.Validators.qnaInterpretInputDataValidator.validate(t);return e?e.map(F):void 0},e.validateExportDataRequest=function(t){var e=n.Validators.exportDataRequestValidator.validate(t);return e?e.map(F):void 0},e.validateVisualHeader=function(t){var e=n.Validators.visualHeaderValidator.validate(t);return e?e.map(F):void 0},e.validateVisualSettings=function(t){var e=n.Validators.visualSettingsValidator.validate(t);return e?e.map(F):void 0},e.validateCommandsSettings=function(t){var e=n.Validators.commandsSettingsValidator.validate(t);return e?e.map(F):void 0},e.validateCustomTheme=function(t){var e=n.Validators.customThemeValidator.validate(t);return e?e.map(F):void 0},e.validateZoomLevel=function(t){var e=n.Validators.zoomLevelValidator.validate(t);return e?e.map(F):void 0}},function(t,e,r){Object.defineProperty(e,"__esModule",{value:!0}),e.Validators=void 0;var i=r(2),a=r(5),o=r(6),n=r(7),l=r(8),s=r(9),u=r(10),d=r(11),c=r(12),p=r(13),f=r(14),h=r(15),v=r(16),y=r(17),m=r(18),V=r(19),g=r(20),b=r(21),w=r(22),P=r(23),_=r(24),O=r(25),S=r(26),T=r(27),E=r(28),F=r(29),R=r(4),x=r(30);e.Validators={addBookmarkRequestValidator:new a.AddBookmarkRequestValidator,advancedFilterTypeValidator:new R.EnumValidator([0]),advancedFilterValidator:new c.AdvancedFilterValidator,anyArrayValidator:new R.ArrayValidator([new S.AnyOfValidator([new R.StringValidator,new R.NumberValidator,new R.BooleanValidator])]),anyFilterValidator:new S.AnyOfValidator([new c.BasicFilterValidator,new c.AdvancedFilterValidator,new c.IncludeExcludeFilterValidator,new c.NotSupportedFilterValidator,new c.RelativeDateFilterValidator,new c.TopNFilterValidator,new c.RelativeTimeFilterValidator]),anyValueValidator:new S.AnyOfValidator([new R.StringValidator,new R.NumberValidator,new R.BooleanValidator]),actionBarValidator:new i.ActionBarValidator,applyBookmarkByNameRequestValidator:new a.ApplyBookmarkByNameRequestValidator,applyBookmarkStateRequestValidator:new a.ApplyBookmarkStateRequestValidator,applyBookmarkValidator:new S.AnyOfValidator([new a.ApplyBookmarkByNameRequestValidator,new a.ApplyBookmarkStateRequestValidator]),backgroundValidator:new R.EnumValidator([0,1]),basicFilterTypeValidator:new R.EnumValidator([1]),basicFilterValidator:new c.BasicFilterValidator,booleanArrayValidator:new R.BooleanArrayValidator,booleanValidator:new R.BooleanValidator,bookmarksPaneValidator:new h.BookmarksPaneValidator,captureBookmarkOptionsValidator:new a.CaptureBookmarkOptionsValidator,captureBookmarkRequestValidator:new a.CaptureBookmarkRequestValidator,commandDisplayOptionValidator:new R.EnumValidator([0,1,2]),commandExtensionSelectorValidator:new S.AnyOfValidator([new b.VisualSelectorValidator,new b.VisualTypeSelectorValidator]),commandExtensionArrayValidator:new R.ArrayValidator([new d.CommandExtensionValidator]),commandExtensionValidator:new d.CommandExtensionValidator,commandsSettingsArrayValidator:new R.ArrayValidator([new o.CommandsSettingsValidator]),commandsSettingsValidator:new o.CommandsSettingsValidator,conditionItemValidator:new c.ConditionItemValidator,contrastModeValidator:new R.EnumValidator([0,1,2,3,4]),customLayoutDisplayOptionValidator:new R.EnumValidator([0,1,2]),customLayoutValidator:new p.CustomLayoutValidator,customPageSizeValidator:new f.CustomPageSizeValidator,customThemeValidator:new n.CustomThemeValidator,dashboardLoadValidator:new l.DashboardLoadValidator,datasetBindingValidator:new s.DatasetBindingValidator,displayStateModeValidator:new R.EnumValidator([0,1]),displayStateValidator:new p.DisplayStateValidator,exportDataRequestValidator:new u.ExportDataRequestValidator,extensionArrayValidator:new R.ArrayValidator([new d.ExtensionValidator]),extensionsValidator:new S.AnyOfValidator([new R.ArrayValidator([new d.ExtensionValidator]),new d.ExtensionsValidator]),extensionPointsValidator:new d.ExtensionPointsValidator,extensionValidator:new d.ExtensionValidator,fieldForbiddenValidator:new T.FieldForbiddenValidator,fieldRequiredValidator:new E.FieldRequiredValidator,fieldsPaneValidator:new h.FieldsPaneValidator,filterColumnTargetValidator:new c.FilterColumnTargetValidator,filterDisplaySettingsValidator:new c.FilterDisplaySettingsValidator,filterConditionsValidator:new R.ArrayValidator([new c.ConditionItemValidator]),filterHierarchyTargetValidator:new c.FilterHierarchyTargetValidator,filterMeasureTargetValidator:new c.FilterMeasureTargetValidator,filterTargetValidator:new S.AnyOfValidator([new c.FilterColumnTargetValidator,new c.FilterHierarchyTargetValidator,new c.FilterMeasureTargetValidator]),filterValidator:new c.FilterValidator,filterTypeValidator:new R.EnumValidator([0,1,2,3,4,5,6,7]),filtersArrayValidator:new R.ArrayValidator([new c.FilterValidator]),filtersOperationsUpdateValidator:new R.EnumValidator([1,2,3]),filtersOperationsRemoveAllValidator:new R.EnumValidator([0]),filtersPaneValidator:new h.FiltersPaneValidator,hyperlinkClickBehaviorValidator:new R.EnumValidator([0,1,2]),includeExcludeFilterValidator:new c.IncludeExcludeFilterValidator,includeExludeFilterTypeValidator:new R.EnumValidator([3]),layoutTypeValidator:new R.EnumValidator([0,1,2,3]),loadQnaValidator:new v.LoadQnaValidator,menuExtensionValidator:new S.AnyOfValidator([new d.FlatMenuExtensionValidator,new d.GroupedMenuExtensionValidator]),menuGroupExtensionArrayValidator:new R.ArrayValidator([new d.MenuGroupExtensionValidator]),menuGroupExtensionValidator:new d.MenuGroupExtensionValidator,menuLocationValidator:new R.EnumValidator([0,1]),notSupportedFilterTypeValidator:new R.EnumValidator([2]),notSupportedFilterValidator:new c.NotSupportedFilterValidator,numberArrayValidator:new R.NumberArrayValidator,numberValidator:new R.NumberValidator,onLoadFiltersBaseValidator:new S.AnyOfValidator([new c.OnLoadFiltersBaseValidator,new c.OnLoadFiltersBaseRemoveOperationValidator]),pageLayoutValidator:new F.MapValidator([new R.StringValidator],[new p.VisualLayoutValidator]),pageNavigationPaneValidator:new h.PageNavigationPaneValidator,pageNavigationPositionValidator:new R.EnumValidator([0,1]),pageSizeTypeValidator:new R.EnumValidator([0,1,2,3,4,5]),pageSizeValidator:new f.PageSizeValidator,pageValidator:new f.PageValidator,pageViewFieldValidator:new f.PageViewFieldValidator,pagesLayoutValidator:new F.MapValidator([new R.StringValidator],[new p.PageLayoutValidator]),paginatedReportCommandsValidator:new o.PaginatedReportCommandsValidator,paginatedReportLoadValidator:new V.PaginatedReportLoadValidator,paginatedReportsettingsValidator:new w.PaginatedReportSettingsValidator,parametersPanelValidator:new x.ParametersPanelValidator,permissionsValidator:new R.EnumValidator([0,1,2,4,7]),playBookmarkRequestValidator:new a.PlayBookmarkRequestValidator,qnaInterpretInputDataValidator:new v.QnaInterpretInputDataValidator,qnaPanesValidator:new h.QnaPanesValidator,qnaSettingValidator:new v.QnaSettingsValidator,relativeDateFilterOperatorValidator:new R.EnumValidator([0,1,2]),relativeDateFilterTimeUnitTypeValidator:new R.EnumValidator([0,1,2,3,4,5,6]),relativeDateFilterTypeValidator:new R.EnumValidator([4]),relativeDateFilterValidator:new c.RelativeDateFilterValidator,relativeDateTimeFilterTypeValidator:new R.EnumValidator([4,7]),relativeDateTimeFilterUnitTypeValidator:new R.EnumValidator([0,1,2,3,4,5,6,7,8]),relativeTimeFilterTimeUnitTypeValidator:new R.EnumValidator([7,8]),relativeTimeFilterTypeValidator:new R.EnumValidator([7]),relativeTimeFilterValidator:new c.RelativeTimeFilterValidator,reportBarsValidator:new i.ReportBarsValidator,reportCreateValidator:new y.ReportCreateValidator,reportLoadFiltersValidator:new S.AnyOfValidator([new R.ArrayValidator([new c.FilterValidator]),new c.OnLoadFiltersValidator]),reportLoadValidator:new m.ReportLoadValidator,reportPanesValidator:new h.ReportPanesValidator,saveAsParametersValidator:new g.SaveAsParametersValidator,selectionPaneValidator:new h.SelectionPaneValidator,settingsValidator:new w.SettingsValidator,singleCommandSettingsValidator:new o.SingleCommandSettingsValidator,slicerSelectorValidator:new S.AnyOfValidator([new b.VisualSelectorValidator,new b.SlicerTargetSelectorValidator]),slicerStateValidator:new P.SlicerStateValidator,slicerTargetValidator:new S.AnyOfValidator([new c.FilterColumnTargetValidator,new c.FilterHierarchyTargetValidator,new c.FilterMeasureTargetValidator,new c.FilterKeyColumnsTargetValidator,new c.FilterKeyHierarchyTargetValidator]),slicerValidator:new P.SlicerValidator,stringArrayValidator:new R.StringArrayValidator,stringValidator:new R.StringValidator,syncSlicersPaneValidator:new h.SyncSlicersPaneValidator,tileLoadValidator:new _.TileLoadValidator,tokenTypeValidator:new R.EnumValidator([0,1]),topNFilterTypeValidator:new R.EnumValidator([5]),topNFilterValidator:new c.TopNFilterValidator,updateFiltersRequestValidator:new S.AnyOfValidator([new c.UpdateFiltersRequestValidator,new c.RemoveFiltersRequestValidator]),viewModeValidator:new R.EnumValidator([0,1]),visualCommandSelectorValidator:new S.AnyOfValidator([new b.VisualSelectorValidator,new b.VisualTypeSelectorValidator]),visualHeaderSelectorValidator:new S.AnyOfValidator([new b.VisualSelectorValidator,new b.VisualTypeSelectorValidator]),visualHeaderSettingsValidator:new O.VisualHeaderSettingsValidator,visualHeaderValidator:new O.VisualHeaderValidator,visualHeadersValidator:new R.ArrayValidator([new O.VisualHeaderValidator]),visualizationsPaneValidator:new h.VisualizationsPaneValidator,visualLayoutValidator:new p.VisualLayoutValidator,visualSelectorValidator:new b.VisualSelectorValidator,visualSettingsValidator:new O.VisualSettingsValidator,visualTypeSelectorValidator:new b.VisualTypeSelectorValidator,zoomLevelValidator:new R.RangeValidator(.25,4)}},function(t,e,r){var i,a=this&&this.__extends||(i=function(t,e){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])})(t,e)},function(t,e){function r(){this.constructor=t}i(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)});Object.defineProperty(e,"__esModule",{value:!0}),e.ActionBarValidator=e.ReportBarsValidator=void 0;var o=r(3),n=r(4),l=r(1),s=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return a(e,t),e.prototype.validate=function(e,r,i){if(null==e)return null;var a=t.prototype.validate.call(this,e,r,i);if(a)return a;var n=[{field:"actionBar",validators:[l.Validators.actionBarValidator]}];return new o.MultipleFieldsValidator(n).validate(e,r,i)},e}(n.ObjectValidator);e.ReportBarsValidator=s;var u=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return a(e,t),e.prototype.validate=function(e,r,i){if(null==e)return null;var a=t.prototype.validate.call(this,e,r,i);if(a)return a;var n=[{field:"visible",validators:[l.Validators.booleanValidator]}];return new o.MultipleFieldsValidator(n).validate(e,r,i)},e}(n.ObjectValidator);e.ActionBarValidator=u},function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.MultipleFieldsValidator=void 0;var r=function(){function t(t){this.fieldValidatorsPairs=t}return t.prototype.validate=function(t,e,r){if(!this.fieldValidatorsPairs)return null;for(var i=e?e+"."+r:r,a=0,o=this.fieldValidatorsPairs;athis.maxValue||e0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0?"&":"?";return t+=i+e+"="+r},e.isSavedInternal=function(t,e,a){return r(this,void 0,void 0,(function(){return i(this,(function(r){switch(r.label){case 0:return r.trys.push([0,2,,3]),[4,t.get("/report/hasUnsavedChanges",{uid:e},a)];case 1:return[2,!r.sent().body];case 2:throw r.sent().body;case 3:return[2]}}))}))},e.isRDLEmbed=function(t){return t&&t.toLowerCase().indexOf("/rdlembed?")>=0},e.autoAuthInEmbedUrl=function(t){return t&&decodeURIComponent(t).toLowerCase().indexOf("autoauth=true")>=0},e.getRandomValue=o,e.getTimeDiffInMilliseconds=function(t,e){return Math.abs(t.getTime()-e.getTime())}},function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.FilterBuilder=void 0;var r=function(){function t(){}return t.prototype.withTargetObject=function(t){return this.target=t,this},t.prototype.withColumnTarget=function(t,e){return this.target={table:t,column:e},this},t.prototype.withMeasureTarget=function(t,e){return this.target={table:t,measure:e},this},t.prototype.withHierarchyLevelTarget=function(t,e,r){return this.target={table:t,hierarchy:e,hierarchyLevel:r},this},t.prototype.withColumnAggregation=function(t,e,r){return this.target={table:t,column:e,aggregationFunction:r},this},t.prototype.withHierarchyLevelAggregationTarget=function(t,e,r,i){return this.target={table:t,hierarchy:e,hierarchyLevel:r,aggregationFunction:i},this},t}();e.FilterBuilder=r},function(t,e){Object.defineProperty(e,"__esModule",{value:!0}),e.EmbedUrlNotSupported=e.APINotSupportedForRDLError=void 0,e.APINotSupportedForRDLError="This API is currently not supported for RDL reports",e.EmbedUrlNotSupported="Embed URL is invalid for this scenario. Please use Power BI REST APIs to get the valid URL"},function(t,e,r){var i,a=this&&this.__extends||(i=function(t,e){return(i=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r])})(t,e)},function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");function r(){this.constructor=t}i(t,e),t.prototype=null===e?Object.create(e):(r.prototype=e.prototype,new r)}),o=this&&this.__awaiter||function(t,e,r,i){return new(r||(r=Promise))((function(a,o){function n(t){try{s(i.next(t))}catch(t){o(t)}}function l(t){try{s(i.throw(t))}catch(t){o(t)}}function s(t){var e;t.done?a(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(n,l)}s((i=i.apply(t,e||[])).next())}))},n=this&&this.__generator||function(t,e){var r,i,a,o,n={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]};return o={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function l(o){return function(l){return function(o){if(r)throw new TypeError("Generator is already executing.");for(;n;)try{if(r=1,i&&(a=2&o[0]?i.return:o[0]?i.throw||((a=i.return)&&a.call(i),0):i.next)&&!(a=a.call(i,o[1])).done)return a;switch(i=0,a&&(o=[2&o[0],a.value]),o[0]){case 0:case 1:a=o;break;case 4:return n.label++,{value:o[1],done:!1};case 5:n.label++,i=o[1],o=[0];continue;case 7:o=n.ops.pop(),n.trys.pop();continue;default:if(!(a=n.trys,(a=a.length>0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1]0&&a[a.length-1])||6!==o[0]&&2!==o[0])){n=0;continue}if(3===o[0]&&(!a||o[1]>a[0]&&o[1] { if (extension?.command) { this.commands.push(extension.command) } }); + extensionsArray.map((extension: models.IExtension) => { if (extension?.command) { this.commands.push(extension.command); } }); } if (isBootstrap) { @@ -707,10 +707,10 @@ export abstract class Embed { * * @hidden */ - private setIframe(isLoad: boolean, phasedRender?: boolean, isBootstrap?: boolean, registerQueryCallback?: boolean): void { + private setIframe(isLoad: boolean, phasedRender?: boolean, isBootstrap?: boolean): void { if (!this.iframe) { const iframeContent = document.createElement("iframe"); - let embedUrl = this.config.uniqueId ? addParamToUrl(this.config.embedUrl, 'uid', this.config.uniqueId) : this.config.embedUrl; + const embedUrl = this.config.uniqueId ? addParamToUrl(this.config.embedUrl, 'uid', this.config.uniqueId) : this.config.embedUrl; iframeContent.style.width = '100%'; iframeContent.style.height = '100%'; diff --git a/src/page.ts b/src/page.ts index b4f48a10..f723fa76 100644 --- a/src/page.ts +++ b/src/page.ts @@ -15,7 +15,9 @@ import { PageLevelFilters, PageSizeType, SectionVisibility, - VisualContainerDisplayMode + VisualContainerDisplayMode, + IPageBackground, + IPageWallpaper, } from 'powerbi-models'; import { IFilterable } from './ifilterable'; import { IReportNode, Report } from './report'; @@ -91,7 +93,7 @@ export class Page implements IPageNode, IFilterable { * * @type {ICustomPageSize} */ - mobileSize: ICustomPageSize; + mobileSize: ICustomPageSize; /** * Page display options as saved in the report. @@ -100,6 +102,20 @@ export class Page implements IPageNode, IFilterable { */ defaultDisplayOption: DisplayOption; + /** + * Page background color. + * + * @type {IPageBackground} + */ + background: IPageBackground; + + /** + * Page wallpaper color. + * + * @type {IPageWallpaper} + */ + wallpaper: IPageWallpaper; + /** * Creates an instance of a Power BI report page. * @@ -110,7 +126,7 @@ export class Page implements IPageNode, IFilterable { * @param {SectionVisibility} [visibility] * @hidden */ - constructor(report: IReportNode, name: string, displayName?: string, isActivePage?: boolean, visibility?: SectionVisibility, defaultSize?: ICustomPageSize, defaultDisplayOption?: DisplayOption, mobileSize?: ICustomPageSize) { + constructor(report: IReportNode, name: string, displayName?: string, isActivePage?: boolean, visibility?: SectionVisibility, defaultSize?: ICustomPageSize, defaultDisplayOption?: DisplayOption, mobileSize?: ICustomPageSize, background?: IPageBackground, wallpaper?: IPageWallpaper) { this.report = report; this.name = name; this.displayName = displayName; @@ -119,6 +135,8 @@ export class Page implements IPageNode, IFilterable { this.defaultSize = defaultSize; this.mobileSize = mobileSize; this.defaultDisplayOption = defaultDisplayOption; + this.background = background; + this.wallpaper = wallpaper; } /** diff --git a/src/report.ts b/src/report.ts index 5c130607..6bb0dc12 100644 --- a/src/report.ts +++ b/src/report.ts @@ -119,7 +119,6 @@ export class Report extends Embed implements IReportNode, IFilterable { return reportId; } - /** * Render a preloaded report, using phased embedding API * @@ -343,7 +342,7 @@ export class Report extends Embed implements IReportNode, IFilterable { try { const response = await this.service.hpm.get('/report/pages', { uid: this.config.uniqueId }, this.iframe.contentWindow); return response.body - .map((page) => new Page(this, page.name, page.displayName, page.isActive, page.visibility, page.defaultSize, page.defaultDisplayOption, page.mobileSize)); + .map((page) => new Page(this, page.name, page.displayName, page.isActive, page.visibility, page.defaultSize, page.defaultDisplayOption, page.mobileSize, page.background, page.wallpaper)); } catch (response) { throw response.body; } @@ -383,7 +382,9 @@ export class Report extends Embed implements IReportNode, IFilterable { page.visibility, page.defaultSize, page.defaultDisplayOption, - page.mobileSize + page.mobileSize, + page.background, + page.wallpaper, ); } catch (response) { throw response.body; @@ -418,7 +419,9 @@ export class Report extends Embed implements IReportNode, IFilterable { activePage.visibility, activePage.defaultSize, activePage.defaultDisplayOption, - activePage.mobileSize + activePage.mobileSize, + activePage.background, + activePage.wallpaper, ); } catch (response) { throw response.body; @@ -567,7 +570,7 @@ export class Report extends Embed implements IReportNode, IFilterable { }; // Set the settings back into the config. - this.config.settings = assign({}, elementAttrSettings, config.settings); + this.config.settings = assign({}, elementAttrSettings, config.settings) as ISettings; if (isBootstrap) { return; @@ -670,6 +673,26 @@ export class Report extends Embed implements IReportNode, IFilterable { return await this.applyThemeInternal({}); } + /** + * get the theme of the report + * + * ```javascript + * report.getTheme(); + * ``` + */ + async getTheme(): Promise { + if (isRDLEmbed(this.config.embedUrl)) { + return Promise.reject(APINotSupportedForRDLError); + } + + try { + const response = await this.service.hpm.get(`/report/theme`, { uid: this.config.uniqueId }, this.iframe.contentWindow); + return response.body; + } catch (response) { + throw response.body; + } + } + /** * Reset user's filters, slicers, and other data view changes to the default state of the report * @@ -738,7 +761,7 @@ export class Report extends Embed implements IReportNode, IFilterable { return Promise.reject(APINotSupportedForRDLError); } - const commandCopy: ICommandExtension[] = JSON.parse(JSON.stringify(this.commands)); + const commandCopy = JSON.parse(JSON.stringify(this.commands)) as ICommandExtension[]; const indexOfCommand: number = this.findCommandMenuIndex("visualContextMenu", commandCopy, commandName, contextMenuTitle); if (indexOfCommand === -1) { throw CommonErrorCodes.NotFound; @@ -808,7 +831,7 @@ export class Report extends Embed implements IReportNode, IFilterable { return Promise.reject(APINotSupportedForRDLError); } - const commandCopy: ICommandExtension[] = JSON.parse(JSON.stringify(this.commands)); + const commandCopy = JSON.parse(JSON.stringify(this.commands)) as ICommandExtension[]; const indexOfCommand: number = this.findCommandMenuIndex("visualOptionsMenu", commandCopy, commandName, optionsMenuTitle); if (indexOfCommand === -1) { @@ -1115,6 +1138,7 @@ export class Report extends Embed implements IReportNode, IFilterable { /** * Return the current zoom level of the report. + * * @returns {Promise} */ async getZoom(): Promise { @@ -1128,6 +1152,7 @@ export class Report extends Embed implements IReportNode, IFilterable { /** * Sets the report's zoom level. + * * @param zoomLevel zoom level to set */ async setZoom(zoomLevel: number): Promise { diff --git a/src/service.ts b/src/service.ts index 375f8c31..eb2e7c4b 100644 --- a/src/service.ts +++ b/src/service.ts @@ -462,7 +462,7 @@ export class Service implements IService { this.router.post(`preQuery`, async (req, _res) => { try { - let result = await applicationContextProvider(req.body); + const result = await applicationContextProvider(req.body); _res.send(200, result); } catch (error) { _res.send(400, null); @@ -589,7 +589,7 @@ export class Service implements IService { * @hidden */ private handleEvent(event: IEvent): void { - let embed = utils.find(embed => { + const embed = utils.find(embed => { return (embed.config.uniqueId === event.id); }, this.embeds); diff --git a/src/util.ts b/src/util.ts index 635899b2..8dc63653 100644 --- a/src/util.ts +++ b/src/util.ts @@ -80,7 +80,7 @@ export function remove(predicate: (x: T) => boolean, xs: T[]): void { * @param {any} args * @returns */ -export function assign(...args) { +export function assign(...args): any { var target = args[0]; 'use strict'; @@ -123,7 +123,7 @@ export function generateUUID(): string { if (typeof performance !== 'undefined' && typeof performance.now === 'function') { d += performance.now(); } - return 'xxxxxxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + return 'xxxxxxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (_c) { // Generate a random number, scaled from 0 to 15. const r = (getRandomValue() % 16); diff --git a/test/SDK-to-HPM.spec.ts b/test/SDK-to-HPM.spec.ts new file mode 100644 index 00000000..591fbd3b --- /dev/null +++ b/test/SDK-to-HPM.spec.ts @@ -0,0 +1,2559 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as utils from '../src/util'; +import * as service from '../src/service'; +import * as embed from '../src/embed'; +import * as report from '../src/report'; +import * as visual from '../src/visual'; +import * as create from '../src/create'; +import * as dashboard from '../src/dashboard'; +import * as page from '../src/page'; +import * as sdkConfig from '../src/config'; +import * as visualDescriptor from '../src/visualDescriptor'; +import * as Wpmp from 'window-post-message-proxy'; +import * as Hpm from 'http-post-message'; +import * as Router from 'powerbi-router'; +import * as models from 'powerbi-models'; +import { spyApp } from './utility/mockEmbed'; +import * as factories from '../src/factories'; +import { spyHpm } from './utility/mockHpm'; +import { spyRouter } from './utility/mockRouter'; +import { APINotSupportedForRDLError } from '../src/errors'; +import { iframeSrc } from './constsants'; + +describe('SDK-to-HPM', function () { + let powerbi: service.Service; + let page1: page.Page; + let visual1: visualDescriptor.VisualDescriptor; + let uniqueId = 'uniqueId'; + let sdkSessionId = 'sdkSessionId'; + let createUniqueId = 'uniqueId'; + let dashboardUniqueId = 'uniqueId'; + let visualUniqueId = 'uniqueId'; + let embedConfiguration: embed.IEmbedConfiguration; + let visualEmbedConfiguration: embed.IVisualEmbedConfiguration; + + beforeEach(function () { + const spyHpmFactory: factories.IHpmFactory = () => { + return spyHpm; + }; + const noop: factories.IWpmpFactory = () => { + return null; + }; + + const spyRouterFactory: factories.IRouterFactory = () => { + return spyRouter; + }; + + spyOn(utils, "getTimeDiffInMilliseconds").and.callFake(() => 700); // Prevent requests from being throttled. + + powerbi = new service.Service(spyHpmFactory, noop, spyRouterFactory, { wpmpName: 'SDK-to-HPM report wpmp' }); + + sdkSessionId = powerbi.getSdkSessionId(); + }); + + afterEach(function () { + spyHpm.get.calls.reset(); + spyHpm.post.calls.reset(); + spyHpm.patch.calls.reset(); + spyHpm.put.calls.reset(); + spyHpm.delete.calls.reset(); + + spyRouter.get.calls.reset(); + spyRouter.post.calls.reset(); + spyRouter.patch.calls.reset(); + spyRouter.put.calls.reset(); + spyRouter.delete.calls.reset(); + + spyApp.reset(); + }); + + describe('report', function () { + let reportElement: HTMLDivElement; + let report: report.Report; + + beforeEach(async () => { + reportElement = document.createElement('div'); + reportElement.className = 'powerbi-report-container'; + document.body.appendChild(reportElement); + + embedConfiguration = { + type: "report", + id: "fakeReportId", + accessToken: 'fakeToken', + embedUrl: iframeSrc + }; + + spyHpm.post.and.callFake(() => Promise.resolve({})); + report = powerbi.embed(reportElement, embedConfiguration); + + page1 = new page.Page(report, 'xyz'); + visual1 = new visualDescriptor.VisualDescriptor(page1, 'uvw', 'title', 'type', {}); + uniqueId = report.config.uniqueId; + + const iframe = reportElement.getElementsByTagName('iframe')[0]; + await new Promise((resolve, _reject) => { + iframe.addEventListener('load', () => resolve(null)); + }); + spyHpm.post.and.callThrough(); + }); + + afterEach(() => { + powerbi.reset(reportElement); + reportElement.remove(); + }); + + describe('load', function () { + it('report.load() sends POST /report/load with configuration in body', async function () { + // Arrange + const testData = { + loadConfiguration: { + id: 'fakeId', + accessToken: 'fakeToken' + }, + response: { + body: null + } + }; + + spyHpm.post.and.returnValue(Promise.resolve(testData.response)); + + // Act + let expectedConfiguration = utils.assign({}, report.config, testData.loadConfiguration); + report.config = expectedConfiguration; + await report.load(); + + // Assert + const expectedHeaders = { + bootstrapped: undefined, + sdkVersion: sdkConfig.default.version, + uid: uniqueId, + sdkSessionId: sdkSessionId + }; + + expect(spyHpm.post).toHaveBeenCalledWith('/report/load', expectedConfiguration, expectedHeaders, jasmine.any(Object)); + }); + + it('report.load() returns promise that rejects with validation error if the load configuration is invalid', async function () { + // Arrange + const testData = { + errorResponse: { + body: { + message: "invalid configuration object" + } + } + }; + + spyHpm.post.and.callFake(() => Promise.reject(testData.errorResponse)); + + try { + // Act + + await report.load(); + fail("load shouldn't succeed"); + } catch (error) { + const expectedHeaders = { + bootstrapped: undefined, + sdkVersion: sdkConfig.default.version, + uid: uniqueId, + sdkSessionId: sdkSessionId + }; + expect(spyHpm.post).toHaveBeenCalledWith('/report/load', report.config, expectedHeaders, jasmine.any(Object)); + expect(error).toEqual(testData.errorResponse.body); + } + }); + + it('report.load() returns promise that resolves with null if the report load successful', async function () { + // Arrange + const testData = { + response: { + body: null + } + }; + + spyHpm.post.and.callFake(() => Promise.resolve(testData.response)); + // Act + try { + const response = await report.load(); + const expectedHeaders = { + bootstrapped: undefined, + sdkVersion: sdkConfig.default.version, + uid: uniqueId, + sdkSessionId: sdkSessionId + }; + + expect(spyHpm.post).toHaveBeenCalledWith('/report/load', report.config, expectedHeaders, jasmine.any(Object)); + expect(response).toEqual(null); + } catch (error) { + console.log("report.load failed with", error); + fail("report.load failed"); + } + }); + + it('report.load() updates the internal configuration if the load request was successful', async function () { + // Arrange + const testData = { + loadConfiguration: { + id: 'newFakeId', + accessToken: 'newFakeToken' + }, + response: { + body: null + } + }; + + spyHpm.post.and.returnValue(Promise.resolve(testData.response)); + + // Act + let expectedConfiguration = { ...report.config, ...testData.loadConfiguration }; + report.config = expectedConfiguration; + try { + const response = await report.load(); + expect(report.config).toEqual(jasmine.objectContaining(expectedConfiguration)); + expect(response).toEqual(null); + } catch (error) { + console.log("report.load failed with", error); + fail("report.load failed"); + } + }); + }); + + describe('pages', function () { + it('report.getPages() sends GET /report/pages', async function () { + // Arrange + const testData = { + response: { + body: [ + { + name: 'page1' + } + ] + } + }; + + spyHpm.get.and.returnValue(Promise.resolve(testData.response)); + + // Act + await report.getPages(); + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.getPages() return promise that rejects with server error if there was error getting pages', async function () { + // Arrange + const testData = { + expectedError: { + body: { + message: 'internal server error' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.getPages(); + fail("getPages should have failed"); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('report.getPages() returns promise that resolves with list of Page objects', async function () { + // Arrange + const testData = { + pages: [ + 'page1', + 'page2' + ], + expectedResponse: { + body: [ + report.page('page1'), + report.page('page2') + ] + } + }; + + spyHpm.get.and.returnValue(Promise.resolve(testData.expectedResponse)); + try { + // Act + const pages = await report.getPages(); + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(pages[0].name).toEqual(testData.expectedResponse.body[0].name); + expect(pages[1].name).toEqual(testData.expectedResponse.body[1].name); + + } catch (error) { + console.log("report.getPages failed with", error); + fail("report.getPages failed"); + } + }); + + it('report.getPageByName() returns promise that rejects if report page with given page name not found', async function () { + // Arrange + const pageName = 'page1'; + const testData = { + expectedError: { + body: { + message: 'page not found' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.getPageByName(pageName); + fail("report.getPages should have failed"); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('report.getPageByName(pageName) returns promise that resolves with page if request is successful', async function () { + // Arrange + const pageName = "page1"; + const testData = { + expectedResponse: + { + report: report, + name: "page1", + displayName: "Page 1", + isActive: true + } + }; + + spyApp.getPageByName.and.returnValue(Promise.resolve(testData.expectedResponse)); + try { + // Act + const page = await spyApp.getPageByName(pageName); + // Assert + expect(spyApp.getPageByName).toHaveBeenCalled(); + expect(page.name).toEqual(testData.expectedResponse.name); + expect(page.isActive).toEqual(testData.expectedResponse.isActive); + } catch (error) { + console.log("getPageByName failed with", error); + fail("getPageByName failed"); + } + }); + + it('report.getActivePage() sends GET /report/pages', async function () { + // Arrange + const testData = { + response: { + body: [ + { + name: 'page1', + isActive: true + } + ] + } + }; + + spyHpm.get.and.returnValue(Promise.resolve(testData.response)); + + // Act + try { + await report.getActivePage(); + } catch (error) { + // The test only checks hpm request + } + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.getActivePage() return promise that rejects with server error if there was error getting active page', async function () { + // Arrange + const testData = { + expectedError: { + body: { + message: 'internal server error' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.getActivePage(); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('report.getActivePage() return promise that rejects if embedded report is an RDL report', async function () { + // Arrange + const testData = { + expectedError: { + body: { + message: APINotSupportedForRDLError + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.getActivePage(); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('report.getActivePage() returns promise that resolves with a page if request is successful', async function () { + // Arrange + const testData = { + expectedResponse: + { + report: report, + name: "page1", + displayName: "Page 1", + isActive: true + } + }; + + spyApp.getActivePage.and.returnValue(Promise.resolve(testData.expectedResponse)); + try { + // Act + const page = await spyApp.getActivePage(); + // Assert + expect(spyApp.getActivePage).toHaveBeenCalled(); + expect(page.name).toEqual(testData.expectedResponse.name); + expect(page.isActive).toEqual(testData.expectedResponse.isActive); + } catch (error) { + console.log("getActivePage failed with", error); + fail("getActivePage failed"); + } + }); + + it('report.addPage() sends POST /report/addPage with displayName', async function () { + // Arrange + const displayName = "testName"; + const expectedRequest = { + displayName: displayName + }; + const expectedHeaders = { uid: uniqueId }; + + spyHpm.post.and.returnValue(Promise.resolve({ body: page1 })); + + // Act + await report.addPage(displayName); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/addPage', expectedRequest, expectedHeaders, jasmine.any(Object)); + }); + + it('report.renamePage() sends PUT /report/pages/{name} with displayName', async function () { + // Arrange + const name = "testName"; + const displayName = "newName"; + const expectedHeaders = { uid: uniqueId }; + const expectedRequest = { + name, + displayName + }; + + spyHpm.put.and.returnValue(Promise.resolve({})); + + // Act + await report.renamePage(name, displayName); + + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${name}/name`, expectedRequest, expectedHeaders, jasmine.any(Object)); + }); + + it('report.deletePage() sends DELETE /report/pages/{name}', async function () { + // Arrange + const name = "testName"; + const expectedHeaders = { uid: uniqueId }; + + spyHpm.delete.and.returnValue(Promise.resolve({})); + + // Act + await report.deletePage(name); + + expect(spyHpm.delete).toHaveBeenCalledWith(`/report/pages/${name}`, {}, expectedHeaders, jasmine.any(Object)); + }); + }); + + describe('filters', function () { + it('report.getFilters() sends GET /report/filters', async function () { + // Arrange + const testData = { + response: { + body: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ] + } + }; + + spyHpm.get.and.returnValue(Promise.resolve(testData.response)); + + // Act + await report.getFilters(); + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/filters', { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.getFilters() returns promise that rejects with server error if there was error getting filters', async function () { + // Arrange + const testData = { + expectedErrors: { + body: [ + { + message: 'target is invalid, missing property x' + } + ] + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedErrors)); + try { + // Act + await report.getFilters(); + } catch (errors) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/filters', { uid: uniqueId }, jasmine.any(Object)); + expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors.body)); + } + }); + + it('report.getFilters() returns promise that resolves with the filters if the request is accepted', async function () { + // Arrange + const testData = { + response: { + body: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ] + } + }; + + spyHpm.get.and.returnValue(Promise.resolve(testData.response)); + + try { + // Act + const filters = await report.getFilters(); + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/filters', { uid: uniqueId }, jasmine.any(Object)); + expect(filters).toEqual(testData.response.body); + } catch (error) { + console.log("getFilters failed with", error); + fail("getFilters failed"); + } + }); + + it('report.setFilters(filters) sends PUT /report/filters', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ] + }; + spyHpm.put.and.returnValue(Promise.resolve({})); + + // Act + await report.setFilters(testData.filters); + + // Assert + expect(spyHpm.put).toHaveBeenCalledWith('/report/filters', testData.filters, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ], + expectedErrors: { + body: [ + { + message: 'target is invalid, missing property x' + } + ] + } + }; + + spyHpm.put.and.callFake(() => Promise.reject(testData.expectedErrors)); + try { + // Act + await report.setFilters(testData.filters); + } catch (errors) { + // Assert + expect(spyHpm.put).toHaveBeenCalledWith('/report/filters', testData.filters, { uid: uniqueId }, jasmine.any(Object)); + expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors.body)); + } + }); + + it('report.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ] + }; + + spyHpm.put.and.returnValue(Promise.resolve(null)); + try { + // Act + const response = await report.setFilters(testData.filters); + expect(spyHpm.put).toHaveBeenCalledWith('/report/filters', testData.filters, { uid: uniqueId }, jasmine.any(Object)); + expect(response).toEqual(null); + } catch (error) { + console.log("setFilters failed with", error); + fail("setFilters failed"); + } + }); + + it('report.removeFilters() sends PUT /report/filters', async function () { + // Arrange + spyHpm.post.and.callFake(() => Promise.resolve({})); + + // Act + await report.removeFilters(); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/filters', { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.removeFilters() returns promise that resolves with null if request is accepted', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve(null)); + + // Act + const response = await report.removeFilters(); + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/filters', { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, jasmine.any(Object)); + expect(response).toEqual(null); + }); + }); + + describe('switchMode', function () { + it('report.switchMode() sends POST /report/switchMode', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.switchMode(models.ViewMode.Edit); + + // Assert + const url = '/report/switchMode/edit'; + expect(spyHpm.post).toHaveBeenCalledWith(url, null, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.switchMode() returns promise that resolves if the request is accepted', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.switchMode(models.ViewMode.Edit); + // Assert + let url = '/report/switchMode/edit'; + expect(spyHpm.post).toHaveBeenCalledWith(url, null, { uid: uniqueId }, jasmine.any(Object)); + }); + }); + + describe('save', function () { + it('report.save() sends POST /report/save', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.save(); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/save', null, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.save() returns promise that resolves if the request is accepted', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.save(); + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/save', null, { uid: uniqueId }, jasmine.any(Object)); + }); + }); + + describe('switchLayout', function () { + it('report.switchLayout(layout) returns promise that rejects with errors if there was error if initial layout and current layout type do not match', async function () { + // Arrange + // Set initial layout to desktop layout + report.config.settings.layoutType = models.LayoutType.Master; + + const layoutType = models.LayoutType.MobileLandscape; + const testData = { + expectedError: { + message: 'Switching between mobile and desktop layouts is not supported. Please reset the embed container and re-embed with required layout.' + }, + settings: { + layoutType: layoutType + } + }; + + spyHpm.patch.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.switchLayout(layoutType); + } catch (error) { + // Assert + expect(spyHpm.patch).not.toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.message); + } + }); + + it('report.switchLayout(layout) returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + // Set initial layout to mobile layout + report.config.settings.layoutType = models.LayoutType.MobilePortrait; + + const layoutType = models.LayoutType.MobileLandscape; + + spyApp.switchLayout.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.switchLayout(layoutType); + // Assert + expect(spyApp.switchLayout).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + }); + + describe('custom layout', function () { + it('visual.moveVisual() returns promise that rejects with server error if error in updating setting', async function () { + // Arrange + const x = 0; + const y = 0; + const testData = { + expectedError: { + body: { + message: 'internal server error' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await visual1.moveVisual(x, y); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('visual.moveVisual() returns promise that resolves with null if request is valid and accepted', async function () { + // Arrange + const x = 0; + const y = 0; + + spyApp.moveVisual.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.moveVisual(x, y); + // Assert + expect(spyApp.moveVisual).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('visual.setVisualDisplayState(displayState) returns promise that rejects with validation error if display state is invalid', async function () { + // Arrange + const displayState = 2; + const testData = { + expectedError: { + body: { + message: 'mode property is invalid' + } + }, + }; + + spyApp.setVisualDisplayState.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await spyApp.setVisualDisplayState(displayState); + fail("setVisualDisplayState should have failed"); + } catch (error) { + // Assert + expect(error).toEqual(testData.expectedError); + } + }); + + it('visual.setVisualDisplayState(displayState) returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const displayState = models.VisualContainerDisplayMode.Visible; + + spyApp.setVisualDisplayState.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.setVisualDisplayState(displayState); + // Assert + expect(spyApp.setVisualDisplayState).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('visual.resizeVisual returns promise that rejects with server error if error in updating setting', async function () { + // Arrange + const width = 200; + const height = 100; + const testData = { + expectedError: { + body: { + message: 'internal server error' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await visual1.resizeVisual(width, height); + fail("resizeVisual should have failed"); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('visual.resizeVisual returns promise that resolves with null if request is valid and accepted', async function () { + // Arrange + const width = 200; + const height = 100; + + spyApp.resizeVisual.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.resizeVisual(width, height); + // Assert + expect(spyApp.resizeVisual).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + }); + + describe('saveAs', function () { + let saveAsParameters: models.ISaveAsParameters = { name: "reportName" }; + + it('report.saveAs() sends POST /report/saveAs', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.saveAs(saveAsParameters); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/saveAs', saveAsParameters, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.saveAs() returns promise that resolves if the request is accepted', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.saveAs(saveAsParameters); + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/saveAs', saveAsParameters, { uid: uniqueId }, jasmine.any(Object)); + + }); + }); + + describe('setAccessToken', function () { + let accessToken: string = "fakeToken"; + + it('report.setAccessToken() sends POST /report/token', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.setAccessToken(accessToken); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/token', accessToken, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.setAccessToken() returns promise that resolves if the request is accepted', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + let newToken = "newToken"; + // Act + await report.setAccessToken(newToken); + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/token', newToken, { uid: uniqueId }, jasmine.any(Object)); + expect(report.service.accessToken).toEqual(newToken); + expect(report.config.accessToken).toEqual(newToken); + expect(report.element.getAttribute(embed.Embed.accessTokenAttribute)).toEqual(newToken); + }); + }); + + describe('print', function () { + it('report.print() sends POST /report/print', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.print(); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/print', null, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('report.print() returns promise that resolves if the request is accepted', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.print(); + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/print', null, { uid: uniqueId }, jasmine.any(Object)); + + }); + }); + + describe('reload', function () { + it('report.reload() sends POST /report/load with configuration in body', async function () { + // Arrange + const testData = { + response: { + body: null + } + }; + + spyHpm.post.and.returnValue(Promise.resolve(testData.response)); + + try { + await report.load(); + spyHpm.post.calls.reset(); + + // Act + try { + await report.reload(); + } catch (error) { + console.log("reloaed failed wtih", error); + fail("reload failed"); + } + + const expectedHeaders = { + bootstrapped: undefined, + sdkVersion: sdkConfig.default.version, + uid: uniqueId, + sdkSessionId: sdkSessionId + }; + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/load', report.config, expectedHeaders, jasmine.any(Object)); + } catch (error) { + console.log("load failed with", error); + fail("load failed"); + } + }); + }); + + describe('refresh', function () { + it('report.refresh() sends POST /report/refresh', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve({ + body: {} + })); + + // Act + await report.refresh(); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/refresh', null, { uid: uniqueId }, jasmine.any(Object)); + }); + }); + + describe('settings', function () { + it('report.updateSettings(settings) sends PATCH /report/settings with settings object', async function () { + // Arrange + const testData = { + settings: { + filterPaneEnabled: false + } + }; + + spyHpm.patch.and.returnValue(Promise.resolve({})); + // Act + try { + await report.updateSettings(testData.settings); + // Assert + expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + } catch (error) { + console.log("updateSettings failed with", error); + fail("updateSettings failed"); + } + }); + + it('report.updateSettings(setting) returns promise that rejects with validation error if object is invalid', async function () { + // Arrange + const testData = { + settings: { + filterPaneEnabled: false + }, + expectedError: { + body: [ + { + message: 'settings object is invalid' + } + ] + } + }; + + spyHpm.patch.and.callFake(() => Promise.reject(testData.expectedError)); + + try { + await report.updateSettings(testData.settings); + fail("updateSettings should have failed"); + } catch (errors) { + // Assert + expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(errors).toEqual(testData.expectedError.body); + } + }); + + it('report.updateSettings(settings) returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const testData = { + settings: { + filterPaneEnabled: false + } + }; + + spyHpm.patch.and.returnValue(Promise.resolve(null)); + + // Act + const response = await report.updateSettings(testData.settings); + // Assert + expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(response).toEqual(null); + }); + + it('report.addContextMenuCommand(commandName, commandTitle, contextMenuTitle) returns promise that rejects with validation errors if extensions property is invalid', async function () { + // Arrange + const commandName = "name1"; + const commandTitle = "title1"; + const contextMenuTitle = "menu1"; + const testData = { + expectedError: { + body: [ + { + message: 'extensions property is invalid' + } + ] + }, + settings: { + extensions: { + commands: [{ + name: "name1", + title: "title1", + extend: { + visualContextMenu: { + title: contextMenuTitle, + menuLocation: 0 + } + } + }], + groups: [] + } + } + }; + + spyHpm.patch.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.addContextMenuCommand(commandName, commandTitle, contextMenuTitle, 0, "", "", ""); + fail("addContextMenuCommand should have failed"); + } catch (error) { + // Assert + expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(jasmine.objectContaining(testData.expectedError.body)); + } + }); + + it('report.addContextMenuCommand(commandName, commandTitle, contextMenuTitle) returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const commandName = "name2"; + const commandTitle = "title2"; + const contextMenuTitle = "menu2"; + + spyApp.addContextMenuCommand.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.addContextMenuCommand(commandName, commandTitle, contextMenuTitle); + // Assert + expect(spyApp.addContextMenuCommand).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('report.addOptionsMenuCommand(commandName, commandTitle, optionsMenuTitle) returns promise that rejects with validation errors if extensions property is invalid', async function () { + // Arrange + const commandName = "name1"; + const commandTitle = "title1"; + const optionsMenuTitle = "menu1"; + const testData = { + expectedError: { + body: [ + { + message: 'extensions property is invalid' + } + ] + }, + settings: { + extensions: { + commands: [{ + name: "name1", + title: "title1", + extend: { + visualOptionsMenu: { + title: "menu1", + menuLocation: 0, + } + }, + icon: undefined + }], + groups: [] + } + } + }; + + spyHpm.patch.and.callFake(() => Promise.reject(testData.expectedError)); + try { + + // Act + await report.addOptionsMenuCommand(commandName, commandTitle, optionsMenuTitle); + fail("addOptionsMenuCommand should have failed"); + } catch (error) { + // Assert + expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(jasmine.objectContaining(testData.expectedError.body)); + } + }); + + it('report.addOptionsMenuCommand(commandName, commandTitle, optionsMenuTitle) returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const commandName = "name2"; + const commandTitle = "title2"; + const optionsMenuTitle = "menu2"; + + spyApp.addOptionsMenuCommand.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.addOptionsMenuCommand(commandName, commandTitle, optionsMenuTitle); + // Assert + expect(spyApp.addOptionsMenuCommand).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('report.removeContextMenuCommand(commandName) returns promise that rejects with validation errors if command name is invalid', async function () { + // Arrange + const commandName = "name1"; + const testData = { + expectedError: { + message: 'PowerBIEntityNotFound' + }, + settings: { + extensions: { + commands: [{ + name: "name1", + title: "title1", + extend: { + } + }] + } + } + }; + + spyApp.removeContextMenuCommand.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await spyApp.removeContextMenuCommand(commandName); + fail("removeContextMenuCommand should have failed"); + } catch (error) { + // Assert + expect(spyHpm.patch).not.toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); + } + }); + + it('report.removeContextMenuCommand(commandName) returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const commandName = "name2"; + + spyApp.removeContextMenuCommand.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.removeContextMenuCommand(commandName); + // Assert + expect(spyApp.removeContextMenuCommand).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('report.removeOptionsMenuCommand(commandName) returns promise that rejects with validation errors if command name is invalid', async function () { + // Arrange + const commandName = "name1"; + const testData = { + expectedError: { + message: 'PowerBIEntityNotFound' + }, + settings: { + extensions: { + commands: [{ + name: "name1", + title: "title1", + icon: "", + extend: { + } + }] + } + } + }; + + spyApp.removeOptionsMenuCommand.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await spyApp.removeOptionsMenuCommand(commandName); + + } catch (error) { + // Assert + expect(spyHpm.patch).not.toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); + } + }); + + it('report.removeOptionsMenuCommand(commandName) returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const commandName = "name2"; + + spyApp.removeOptionsMenuCommand.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.removeOptionsMenuCommand(commandName); + // Assert + expect(spyApp.removeOptionsMenuCommand).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('report.setVisualDisplayState(pageName, visualName, displayState) returns promise that rejects with validation error if display state is invalid', async function () { + // Arrange + const pageName = 'page1'; + const visualName = 'visual'; + const displayState = 2; + const testData = { + expectedError: { + body: { + message: 'display state is invalid' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.setVisualDisplayState(pageName, visualName, displayState); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('report.setVisualDisplayState(pageName, visualName, displayState) returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const pageName = 'page1'; + const visualName = 'visual'; + const displayState = models.VisualContainerDisplayMode.Visible; + + spyApp.setVisualDisplayState.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.setVisualDisplayState(pageName, visualName, displayState); + // Assert + expect(spyApp.setVisualDisplayState).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('report.resizeVisual returns promise that rejects with validation error if page name is invalid', async function () { + // Arrange + const pageName = 'invalid page'; + const visualName = 'visual'; + const width = 200; + const height = 100; + const testData = { + expectedError: { + body: { + message: 'page name is invalid' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.resizeVisual(pageName, visualName, width, height); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('report.resizeVisual returns promise that resolves with null if request is valid and accepted', async function () { + // Arrange + const pageName = 'page1'; + const visualName = 'visual'; + const width = 200; + const height = 100; + + spyApp.resizeVisual.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.resizeVisual(pageName, visualName, width, height); + // Assert + expect(spyApp.resizeVisual).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('report.resizeActivePage returns promise that rejects with validation error if page size type is invalid', async function () { + // Arrange + const pageSizeType = 5; + const width = 200; + const height = 100; + const testData = { + expectedError: { + body: { + message: 'page size type is invalid' + } + }, + settings: { + layoutType: models.LayoutType.Custom, + customLayout: { + pageSize: { + type: pageSizeType, + width: width, + height: height + } + } + } + }; + + spyHpm.patch.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.resizeActivePage(pageSizeType, width, height); + } catch (error) { + // Assert + expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('report.resizeActivePage returns promise that resolves with null if request is valid and accepted', async function () { + // Arrange + const pageSizeType = models.PageSizeType.Custom; + const width = 200; + const height = 100; + + spyApp.resizeActivePage.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.resizeActivePage(pageSizeType, width, height); + // Assert + expect(spyApp.resizeActivePage).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('moveVisual returns promise that rejects with validation error if visual name is invalid', async function () { + // Arrange + const pageName = 'page1'; + const visualName = 'invalid visual'; + const x = 0; + const y = 0; + const testData = { + expectedError: { + body: { + message: 'visual name is invalid' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await report.moveVisual(pageName, visualName, x, y); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('moveVisual returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const pageName = 'page1'; + const visualName = 'visual'; + const x = 0; + const y = 0; + + spyApp.moveVisual.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.moveVisual(pageName, visualName, x, y); + // Assert + expect(spyApp.moveVisual).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + }); + + describe('visual level filters', function () { + it('visual.getFilters() sends GET /report/pages/xyz/visuals/uvw/filters', async function () { + // Arrange + spyHpm.get.and.callFake(() => Promise.resolve({})); + + // Act + await visual1.getFilters(); + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('visual.getFilters() return promise that rejects with server error if there was error getting filters', async function () { + // Arrange + const testData = { + expectedError: { + body: { + message: 'internal server error' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + + try { + // Act + await visual1.getFilters(); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('visual.getFilters() returns promise that resolves with list of filters', async function () { + // Arrange + const testData = { + expectedResponse: { + body: [ + { x: 'fakeFilter1' }, + { x: 'fakeFilter2' } + ] + } + }; + + spyHpm.get.and.returnValue(Promise.resolve(testData.expectedResponse)); + + // Act + const filters = await visual1.getFilters(); + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { uid: uniqueId }, jasmine.any(Object)); + // @ts-ignore as testData is not of type IFilter + expect(filters).toEqual(testData.expectedResponse.body); + }); + + it('visual.setFilters(filters) sends PUT /report/pages/xyz/visuals/uvw/filters', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ], + response: { + body: [] + } + }; + + spyHpm.put.and.returnValue(Promise.resolve(testData.response)); + + // Act + await visual1.setFilters(testData.filters); + + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, testData.filters, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('visual.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ], + expectedErrors: { + body: [ + { + message: 'target is invalid, missing property x' + } + ] + } + }; + + spyHpm.put.and.callFake(() => Promise.reject(testData.expectedErrors)); + try { + // Act + await visual1.setFilters(testData.filters); + fail("setFilters shouldn't succeed"); + } catch (errors) { + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, testData.filters, { uid: uniqueId }, jasmine.any(Object)); + expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors.body)); + } + }); + + it('visual.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ] + }; + + spyHpm.put.and.returnValue(Promise.resolve(null)); + try { + // Act + const response = await visual1.setFilters(testData.filters); + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, testData.filters, { uid: uniqueId }, jasmine.any(Object)); + expect(response).toEqual(null); + } catch (error) { + console.log("setFilters failed with", error); + fail("setFilters failed"); + } + }); + + it('visual.removeFilters() sends PUT /report/pages/xyz/visuals/uvw/filters', async function () { + // Arrange + + // Act + await visual1.removeFilters(); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('visual.removeFilters() returns promise that resolves with null if request is accepted', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve(null)); + + // Act + const response = await visual1.removeFilters(); + // Assert + expect(spyHpm.post).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, jasmine.any(Object)); + expect(response).toEqual(null); + }); + }); + + describe('page', function () { + describe('filters', function () { + it('page.getFilters() sends GET /report/pages/xyz/filters', async function () { + // Arrange + spyHpm.get.and.callFake(() => Promise.resolve({})); + + // Act + await page1.getFilters(); + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('page.getFilters() return promise that rejects with server error if there was error getting filters', async function () { + // Arrange + const testData = { + expectedError: { + body: { + message: 'internal server error' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await page1.getFilters(); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('page.getFilters() returns promise that resolves with list of filters', async function () { + // Arrange + const testData = { + expectedResponse: { + body: [ + { x: 'fakeFilter1' }, + { x: 'fakeFilter2' } + ] + } + }; + + spyHpm.get.and.returnValue(Promise.resolve(testData.expectedResponse)); + + // Act + const filters = await page1.getFilters(); + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { uid: uniqueId }, jasmine.any(Object)); + // @ts-ignore as testData is not of type IFilter + expect(filters).toEqual(testData.expectedResponse.body); + }); + + it('page.setFilters(filters) sends PUT /report/pages/xyz/filters', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ], + response: { + body: [] + } + }; + + spyHpm.put.and.returnValue(Promise.resolve(testData.response)); + + // Act + await page1.setFilters(testData.filters); + + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, testData.filters, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('page.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ], + expectedErrors: { + body: [ + { + message: 'target is invalid, missing property x' + } + ] + } + }; + + spyHpm.put.and.callFake(() => Promise.reject(testData.expectedErrors)); + try { + // Act + await page1.setFilters(testData.filters); + } catch (errors) { + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, testData.filters, { uid: uniqueId }, jasmine.any(Object)); + expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors.body)); + } + }); + + it('page.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ] + }; + + spyHpm.put.and.returnValue(Promise.resolve(null)); + + // Act + const response = await page1.setFilters(testData.filters); + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, testData.filters, { uid: uniqueId }, jasmine.any(Object)); + expect(response).toEqual(null); + }); + + it('page.removeFilters() sends PUT /report/pages/xyz/filters', async function () { + // Arrange + + // Act + await page1.removeFilters(); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('page.removeFilters() returns promise that resolves with null if request is accepted', async function () { + // Arrange + spyHpm.post.and.returnValue(Promise.resolve(null)); + + // Act + const response = await page1.removeFilters(); + // Assert + expect(spyHpm.post).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, jasmine.any(Object)); + expect(response).toEqual(null); + }); + }); + + describe('setActive', function () { + it('page.setActive() sends PUT /report/pages/active', async function () { + // Arrange + const testData = { + page: { + name: page1.name, + displayName: null, + isActive: true, + } + }; + + spyHpm.put.and.returnValue(Promise.resolve(null)); + + // Act + await page1.setActive(); + + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/active`, testData.page, { uid: uniqueId }, jasmine.any(Object)); + }); + + it('page.setActive() returns a promise rejected with errors if the page was invalid', async function () { + // Arrange + const testData = { + page: { + name: page1.name, + displayName: null, + isActive: true, + }, + response: { + body: [ + { + message: 'page abc123 does not exist on report xyz' + } + ] + } + }; + + spyHpm.put.and.callFake(() => Promise.reject(testData.response)); + try { + // Act + await page1.setActive(); + fail("setActive should have failed"); + } catch (errors) { + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/active`, testData.page, { uid: uniqueId }, jasmine.any(Object)); + expect(errors).toEqual(jasmine.objectContaining(testData.response.body)); + } + }); + + it('page.setActive() returns a promise resolved with null if the page is valid', async function () { + // Arrange + const testData = { + page: { + name: page1.name, + displayName: null, + isActive: true, + } + }; + + spyHpm.put.and.returnValue(Promise.resolve(null)); + + // Act + const response = await page1.setActive(); + + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/active`, testData.page, { uid: uniqueId }, jasmine.any(Object)); + expect(response).toEqual(null); + }); + }); + describe('custom layout', function () { + it('page.setVisualDisplayState returns promise that rejects with validation error if display state is invalid', async function () { + // Arrange + const visualName = 'visual'; + const displayState = 2; + const testData = { + expectedError: { + body: { + message: 'display state is invalid' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await page1.setVisualDisplayState(visualName, displayState); + fail("setVisualDisplayState should have failed"); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('page.setVisualDisplayState returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const visualName = 'visual'; + const displayState = models.VisualContainerDisplayMode.Visible; + + spyApp.setVisualDisplayState.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.setVisualDisplayState(visualName, displayState); + // Assert + expect(spyApp.setVisualDisplayState).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + + it('page.moveVisual returns promise that rejects with validation error if visual name is invalid', async function () { + // Arrange + const visualName = 'invalid visual'; + const x = 0; + const y = 0; + const testData = { + expectedError: { + body: { + message: 'visual name is invalid' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await page1.moveVisual(visualName, x, y); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('page.moveVisual returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const visualName = 'visual'; + const x = 0; + const y = 0; + + spyApp.moveVisual.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.moveVisual(visualName, x, y); + // Assert + expect(spyApp.moveVisual).toHaveBeenCalled(); + expect(response).toEqual(null); + + }); + + it('page.resizePage returns promise that rejects with validation error if page is not active page', async function () { + // Arrange=- + const pageSizeType = 1; + const width = 200; + const height = 100; + const testData = { + expectedError: { + message: 'Cannot resize the page. Only the active page can be resized' + }, + settings: { + layoutType: models.LayoutType.Custom, + customLayout: { + pageSize: { + type: pageSizeType, + width: width, + height: height + } + } + } + }; + + spyHpm.patch.and.callFake(() => Promise.reject(testData.expectedError.message)); + try { + // Act + await page1.resizePage(pageSizeType, width, height); + } catch (error) { + // Assert + expect(spyHpm.patch).not.toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.message); + } + }); + + it('page.resizePage returns promise that resolves with null if page is active page', async function () { + // Arrange + const pageSizeType = 1; + const width = 200; + const height = 100; + const testData = { + settings: { + layoutType: models.LayoutType.Custom, + customLayout: { + pageSize: { + type: pageSizeType, + width: width, + height: height + } + } + } + }; + + page1.isActive = true; + spyHpm.patch.and.returnValue(Promise.resolve(null)); + + // Act + const response = await page1.resizePage(pageSizeType, width, height); + // Assert + expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, jasmine.any(Object)); + expect(response).toEqual(null); + }); + + it('page.resizePage returns promise that resolves with null if request is valid and accepted', async function () { + // Arrange + const pageSizeType = models.PageSizeType.Custom; + const width = 200; + const height = 100; + + spyApp.resizeActivePage.and.returnValue(Promise.resolve(null)); + + // Act + const response = await spyApp.resizeActivePage(pageSizeType, width, height); + // Assert + expect(spyApp.resizeActivePage).toHaveBeenCalled(); + expect(response).toEqual(null); + }); + }); + }); + + describe('setDisplayName', function () { + it('page.setDisplayName(displayName) sends PUT /report/pages/{pageName}/name', async function () { + // Arrange + const displayName = "newName"; + const testData = { + page: { + name: page1.name, + displayName, + } + }; + + spyHpm.put.and.returnValue(Promise.resolve(null)); + + // Act + await page1.setDisplayName(displayName); + + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/name`, testData.page, { uid: uniqueId }, jasmine.any(Object)); + }); + }); + + describe('getVisualByName', function () { + it('page.getVisualByName(visualName) returns promise that rejects if visual with given name not found', async function () { + // Arrange + const pageName = page1.name; + const visualName = "visual1"; + const testData = { + expectedError: { + body: { + message: 'visual not found' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await page1.getVisualByName(visualName); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${pageName}/visuals`, { uid: uniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('page.getVisualByName(visualName) returns promise that resolves with visual if request is successful', async function () { + // Arrange + const visualName = "visual1"; + const testData = { + expectedResponse: + { + name: "visual1", + title: "Visual 1", + type: "type1", + layout: {}, + page: {} + } + }; + + spyApp.getVisualByName.and.returnValue(Promise.resolve(testData.expectedResponse)); + + // Act + const visual = await spyApp.getVisualByName(visualName); + + // Assert + expect(spyApp.getVisualByName).toHaveBeenCalled(); + expect(visual.name).toEqual(testData.expectedResponse.name); + expect(visual.title).toEqual(testData.expectedResponse.title); + }); + }); + + describe('SDK-to-Router (Event subscription)', function () { + /** + * This test should likely be moved to mock app section or removed since it is already covered. + * The validation of supported events should likely happen by powerbi instead of by the SDK + * since this is maitanence problem + */ + it(`report.on(eventName, handler) should throw error if eventName is not supported`, function () { + // Arrange + const testData = { + eventName: 'xyz', + handler: jasmine.createSpy('handler') + }; + + // Act + const attemptToSubscribeToEvent = (): void => { + report.on(testData.eventName, testData.handler); + }; + + // Assert + expect(attemptToSubscribeToEvent).toThrowError(); + }); + }); + + describe('theme', function () { + it('report.getTheme() sends GET /report/theme', async function () { + // Arrange + const testData = { + response: { + body: [ + { + themeJson: {name: "Theme ABC 123"} + } + ] + } + }; + + const expectedHeaders = { + uid: uniqueId, + }; + + spyHpm.get.and.returnValue(Promise.resolve(testData.response)); + try { + await report.getTheme(); + expect(spyHpm.put).toHaveBeenCalledWith('/report/theme', expectedHeaders, jasmine.any(Object)); + } catch (error) { + console.log("getTheme failed with", error); + fail("getTheme failed"); + } + }); + + it('report.applyTheme(theme) sends PUT /report/theme with theme in body', async function () { + // Arrange + const testData = { + theme: { + themeJson: { + name: "Theme ABC 123" + } + }, + response: { + body: null + } + }; + + const expectedHeaders = { + uid: uniqueId, + }; + + spyHpm.put.and.returnValue(Promise.resolve(testData.response)); + try { + await report.applyTheme(testData.theme); + expect(spyHpm.put).toHaveBeenCalledWith('/report/theme', jasmine.objectContaining(testData.theme), expectedHeaders, jasmine.any(Object)); + } catch (error) { + console.log("applyTheme failed with", error); + fail("applyTheme failed"); + } + }); + + it('report.resetTheme() sends PUT /report/theme with empty object as theme in body', async function () { + // Arrange + const response = { + body: null + }; + + const expectedHeaders = { + uid: uniqueId, + }; + + spyHpm.put.and.returnValue(Promise.resolve(response)); + + try { + await report.resetTheme(); + expect(spyHpm.put).toHaveBeenCalledWith('/report/theme', {}, expectedHeaders, jasmine.any(Object)); + } catch (error) { + console.log("resetTheme failed with", error); + fail("resetTheme failed"); + } + }); + }); + }); + + describe('createReport', function () { + let createElement: HTMLDivElement; + let create: create.Create; + + beforeEach(async () => { + createElement = document.createElement('div'); + createElement.className = 'powerbi-create-container'; + document.body.appendChild(createElement); + + const embedCreateConfiguration = { + datasetId: "fakeReportId", + accessToken: 'fakeToken', + embedUrl: iframeSrc + }; + spyHpm.post.and.returnValue(Promise.resolve({})); + create = powerbi.createReport(createElement, embedCreateConfiguration); + createUniqueId = create.config.uniqueId; + const createIframe = createElement.getElementsByTagName('iframe')[0]; + await new Promise((resolve, _reject) => createIframe.addEventListener('load', () => resolve(null))); + spyHpm.post.and.callThrough(); + }); + + afterEach(() => { + powerbi.reset(createElement); + createElement.remove(); + }); + + it('create.createReport() sends POST /report/create with configuration in body', async function () { + // Arrange + const testData = { + createConfiguration: { + datasetId: 'fakeId', + accessToken: 'fakeToken' + }, + response: { + body: null + } + }; + + spyHpm.post.and.returnValue(Promise.resolve(testData.response)); + + // Act + await create.createReport(testData.createConfiguration); + + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/create', testData.createConfiguration, { uid: createUniqueId, sdkSessionId: sdkSessionId }, jasmine.any(Object)); + }); + + it('create.createReport() returns promise that rejects with validation error if the create configuration is invalid', async function () { + // Arrange + const testData = { + createConfiguration: { + datasetId: 'fakeId', + accessToken: 'fakeToken' + }, + errorResponse: { + body: { + message: "invalid configuration object" + } + } + }; + + spyHpm.post.and.callFake(() => Promise.reject(testData.errorResponse)); + try { + // Act + await create.createReport(testData.createConfiguration); + + } catch (error) { + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/create', testData.createConfiguration, { uid: createUniqueId, sdkSessionId: sdkSessionId }, jasmine.any(Object)); + expect(error).toEqual(testData.errorResponse.body); + } + }); + + it('create.createReport() returns promise that resolves with null if create report was successful', async function () { + // Arrange + const testData = { + createConfiguration: { + datasetId: 'fakeId', + accessToken: 'fakeToken' + }, + response: { + body: null + } + }; + + spyHpm.post.and.returnValue(Promise.resolve(testData.response)); + + // Act + const response = await create.createReport(testData.createConfiguration); + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/report/create', testData.createConfiguration, { uid: createUniqueId, sdkSessionId: sdkSessionId }, jasmine.any(Object)); + expect(response).toEqual(null); + }); + }); + + describe('dashboard', function () { + let dashboardElement: HTMLDivElement; + let dashboard: dashboard.Dashboard; + + beforeEach(async () => { + dashboardElement = document.createElement('div'); + dashboardElement.className = 'powerbi-dashboard-container'; + document.body.appendChild(dashboardElement); + + const dashboardEmbedConfiguration = { + type: "dashboard", + id: "fakeDashboardId", + accessToken: 'fakeToken', + embedUrl: iframeSrc + }; + spyHpm.post.and.returnValue(Promise.resolve({})); + dashboard = powerbi.embed(dashboardElement, dashboardEmbedConfiguration); + dashboardUniqueId = dashboard.config.uniqueId; + const dashboardIframe = dashboardElement.getElementsByTagName('iframe')[0]; + await new Promise((resolve, _reject) => { + dashboardIframe.addEventListener('load', () => resolve(null)); + }); + spyHpm.post.and.callThrough(); + }); + + afterEach(() => { + powerbi.reset(dashboardElement); + dashboardElement.remove(); + }); + + it('dashboard.load() sends POST /dashboard/load with configuration in body', async function () { + spyHpm.post.and.returnValue(Promise.resolve({})); + try { + // Act + dashboard.iframeLoaded = true; + await dashboard.load(); + const expectedHeaders = { + bootstrapped: undefined, + sdkVersion: sdkConfig.default.version, + uid: dashboardUniqueId, + sdkSessionId: sdkSessionId + }; + expect(spyHpm.post).toHaveBeenCalled(); + // Assert + expect(spyHpm.post).toHaveBeenCalledWith('/dashboard/load', dashboard.config, expectedHeaders, jasmine.any(Object)); + + } catch (error) { + console.log("dashboard load failed with", error); + fail("dashboard load failed"); + } + }); + }); + + describe('visual', function () { + let visualElement: HTMLDivElement; + let embeddedVisual: visual.Visual; + + beforeEach(async () => { + visualElement = document.createElement('div'); + visualElement.className = 'powerbi-report-container'; + document.body.appendChild(visualElement); + + visualEmbedConfiguration = { + id: "visual1", + accessToken: 'fakeToken', + embedUrl: iframeSrc, + type: "visual", + pageName: "ReportSection1", + visualName: "VisualContainer1", + width: 1200, + height: 1000 + }; + spyHpm.post.and.callFake(() => Promise.resolve({})); + embeddedVisual = powerbi.embed(visualElement, visualEmbedConfiguration) as any as visual.Visual; + visualUniqueId = embeddedVisual.config.uniqueId; + const visualFrame = visualElement.getElementsByTagName('iframe')[0]; + await new Promise((resolve, _reject) => { + visualFrame.addEventListener('load', () => resolve(null)); + }); + spyHpm.post.and.callThrough(); + }); + + afterEach(() => { + powerbi.reset(visualElement); + visualElement.remove(); + }); + + it('powerbi.embed with visual name sends POST /report/load with custom layout configuration in body', async function () { + + let testData = { + loadConfiguration: visualEmbedConfiguration, + response: { + body: null + } + }; + + let expectedConfiguration = { + id: visualEmbedConfiguration.id, + accessToken: visualEmbedConfiguration.accessToken, + embedUrl: visualEmbedConfiguration.embedUrl, + type: visualEmbedConfiguration.type, + pageName: visualEmbedConfiguration.pageName, + visualName: visualEmbedConfiguration.visualName, + width: visualEmbedConfiguration.width, + height: visualEmbedConfiguration.height, + groupId: undefined, + uniqueId: embeddedVisual.config.uniqueId, + settings: { + filterPaneEnabled: false, + navContentPaneEnabled: false, + layoutType: models.LayoutType.Custom, + customLayout: { + displayOption: models.DisplayOption.FitToPage, + pageSize: { + type: models.PageSizeType.Custom, + width: testData.loadConfiguration.width, + height: testData.loadConfiguration.height, + }, + pagesLayout: { + ReportSection1: { + defaultLayout: { + displayState: { + mode: models.VisualContainerDisplayMode.Hidden + } + }, + visualsLayout: { + VisualContainer1: { + displayState: { + mode: models.VisualContainerDisplayMode.Visible + }, + x: 1, + y: 1, + z: 1, + width: testData.loadConfiguration.width, + height: testData.loadConfiguration.height + } + } + } + } + } + } + }; + + spyHpm.post.and.returnValue(Promise.resolve(testData.response)); + + // Act + let inputConfig = utils.assign({}, embeddedVisual.config, visualEmbedConfiguration); + embeddedVisual.config = inputConfig; + + await embeddedVisual.load(); + // Assert + expect(spyHpm.post).toHaveBeenCalled(); + + let spyArgs = spyHpm.post.calls.mostRecent().args; + expect(spyArgs[0]).toEqual('/report/load'); + expect(spyArgs[1]).toEqual(expectedConfiguration); + expect(spyArgs[2]).toEqual({ + bootstrapped: undefined, + sdkVersion: sdkConfig.default.version, + uid: visualUniqueId, + sdkSessionId: sdkSessionId + }); + }); + + it('embeddedVisual.getFilters(models.FiltersLevel.Report) sends GET /report/filters', async function () { + spyHpm.get.and.callFake(() => Promise.resolve({})); + + // Act + await embeddedVisual.getFilters(models.FiltersLevel.Report); + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith('/report/filters', { uid: visualUniqueId }, jasmine.any(Object)); + }); + + it('embeddedVisual.setFilters(filters, models.FiltersLevel.Report) sends PUT /report/filters', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ] + }; + + // Act + await embeddedVisual.setFilters(testData.filters, models.FiltersLevel.Report); + + // Assert + expect(spyHpm.put).toHaveBeenCalledWith('/report/filters', testData.filters, { uid: visualUniqueId }, jasmine.any(Object)); + }); + + it('embeddedVisual.getFilters(models.FiltersLevel.Page) sends GET /report/pages/ReportSection1/filters', async function () { + spyHpm.get.and.callFake(() => Promise.resolve({})); + + // Act + await embeddedVisual.getFilters(models.FiltersLevel.Page); + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/filters`, { uid: visualUniqueId }, jasmine.any(Object)); + }); + + it('embeddedVisual.setFilters(filters, models.FiltersLevel.Page) sends PUT /report/pages/ReportSection1/filters', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ], + response: { + body: [] + } + }; + + spyHpm.put.and.returnValue(Promise.resolve(testData.response)); + + // Act + await embeddedVisual.setFilters(testData.filters, models.FiltersLevel.Page); + + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/ReportSection1/filters`, testData.filters, { uid: visualUniqueId }, jasmine.any(Object)); + }); + + it('embeddedVisual.getFilters() sends GET /report/pages/ReportSection1/visuals/VisualContainer1/filters', async function () { + spyHpm.get.and.callFake(() => Promise.resolve({})); + + // Act + await embeddedVisual.getFilters(); + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals/VisualContainer1/filters`, { uid: visualUniqueId }, jasmine.any(Object)); + }); + + it('embeddedVisual.setFilters(filters) sends PUT /report/pages/ReportSection1/visuals/VisualContainer1/filters', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), + (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() + ], + response: { + body: [] + } + }; + + spyHpm.put.and.returnValue(Promise.resolve(testData.response)); + + // Act + await embeddedVisual.setFilters(testData.filters); + + // Assert + expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals/VisualContainer1/filters`, testData.filters, { uid: visualUniqueId }, jasmine.any(Object)); + }); + + it('Not supported visual method: getPages', function () { + // Act + const attempt = (): void => { + embeddedVisual.getPages(); + }; + + // Assert + expect(attempt).toThrow(visual.Visual.GetPagesNotSupportedError); + }); + + it('Not supported visual method: setPage', function () { + // Act + const attempt = (): void => { + embeddedVisual.setPage(null); + }; + + // Assert + expect(attempt).toThrow(visual.Visual.SetPageNotSupportedError); + }); + + describe('getVisualDescriptor', function () { + it('embeddedVisual.getVisualDescriptor() sends GET /report/pages/xyz/visuals', async function () { + // Act + try { + await embeddedVisual.getVisualDescriptor(); + } catch (error) { + // The test only needs to check hpm request + } + + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals`, { uid: visualUniqueId }, jasmine.any(Object)); + }); + + it('embeddedVisual.getVisualDescriptor() returns promise that rejects with server error if there was error getting visual details', async function () { + // Arrange + const testData = { + expectedError: { + body: { + message: 'internal server error' + } + } + }; + + spyHpm.get.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await embeddedVisual.getVisualDescriptor(); + fail("getVisualDescriptor should succeed"); + } catch (error) { + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals`, { uid: visualUniqueId }, jasmine.any(Object)); + expect(error).toEqual(testData.expectedError.body); + } + }); + + it('embeddedVisual.getVisualDescriptor() returns promise that resolves with visual details', async function () { + // Arrange + const fakeVisualDescriptor = new visualDescriptor.VisualDescriptor(page1, visualEmbedConfiguration.visualName, 'title', 'type', {}); + const testData = { + expectedResponse: { + body: [fakeVisualDescriptor] + } + }; + + spyHpm.get.and.returnValue(Promise.resolve(testData.expectedResponse)); + + try { + // Act + const visualDescriptor = await embeddedVisual.getVisualDescriptor(); + // Assert + expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals`, { uid: visualUniqueId }, jasmine.any(Object)); + expect(visualDescriptor.name).toEqual(fakeVisualDescriptor.name); + } catch (error) { + console.log("getVisualDescriptor failed with", error); + fail("getVisualDescriptor failed"); + } + }); + }); + }); +}); diff --git a/test/SDK-to-MockApp.spec.ts b/test/SDK-to-MockApp.spec.ts new file mode 100644 index 00000000..604896ed --- /dev/null +++ b/test/SDK-to-MockApp.spec.ts @@ -0,0 +1,598 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as utils from '../src/util'; +import * as service from '../src/service'; +import * as embed from '../src/embed'; +import * as report from '../src/report'; +import * as page from '../src/page'; +import * as Hpm from 'http-post-message'; +import * as models from 'powerbi-models'; +import * as factories from '../src/factories'; +import * as util from '../src/util'; +import { spyApp, setupEmbedMockApp } from './utility/mockEmbed'; +import { logMessages, iframeSrc } from './constsants'; + +describe('SDK-to-MockApp', function () { + let element: HTMLDivElement; + let iframe: HTMLIFrameElement; + let iframeHpm: Hpm.HttpPostMessage; + let powerbi: service.Service; + let report: report.Report; + let page1: page.Page; + const embedConfiguration: embed.IEmbedConfiguration = { + type: "report", + id: "fakeReportIdInitialEmbed", + accessToken: 'fakeTokenInitialEmbed', + embedUrl: iframeSrc + }; + + beforeEach(async function () { + powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory, { + wpmpName: 'SDK-to-MockApp HostWpmp', + logMessages + }); + element = document.createElement('div'); + element.id = "reportContainer1"; + element.className = 'powerbi-report-container2'; + document.body.appendChild(element); + report = powerbi.embed(element, embedConfiguration); + page1 = report.page('ReportSection1'); + iframe = element.getElementsByTagName('iframe')[0]; + + /** + * Note: For testing we need to configure the eventSourceOverrideWindow to allow the host to respond to + * the iframe window; however, the iframe window doesn't exist until the first embed is created. + * + * To work around this we create a service for the initial embed, embed a report, then set the private variable + */ + (powerbi.wpmp).eventSourceOverrideWindow = iframe.contentWindow; + // Register Iframe side + let hpmPostSpy = spyOn(powerbi.hpm, "post").and.returnValue(Promise.resolve({})); + iframeHpm = setupEmbedMockApp(iframe.contentWindow, window, logMessages, 'SDK-to-MockApp IframeWpmp'); + + await new Promise((resolve, _reject) => { + iframe.addEventListener('load', () => { + resolve(null); + }); + }); + + hpmPostSpy.and.callThrough(); + }); + + afterEach(function () { + powerbi.reset(element); + element.remove(); + powerbi.wpmp?.stop(); + spyApp.reset(); + }); + + describe('report', function () { + beforeEach(function () { + spyOn(utils, "getTimeDiffInMilliseconds").and.callFake(() => 700); // Prevent requests from being throttled. + }); + + describe('load', function () { + it('report.load() returns promise that resolves with null if the report load successful', async function () { + try { + const response = await report.load(undefined); + // Assert + expect(response).toEqual({} as any); + } catch (error) { + fail("lod shouldn't fail"); + } + }); + }); + + describe('pages', function () { + it('report.getPages() return promise that rejects with server error if there was error getting pages', async function () { + // Arrange + const testData = { + expectedError: { + message: 'internal server error' + } + }; + + try { + spyApp.getPages.and.callFake(() => Promise.reject(testData.expectedError)); + // Act + await report.getPages(); + fail("getPagesshouldn't succeed"); + } catch (error) { + // Assert + expect(spyApp.getPages).toHaveBeenCalled(); + expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); + } + }); + + it('report.getPages() returns promise that resolves with list of page names', async function () { + // Arrange + const testData = { + pages: [ + { + name: "page1", + displayName: "Page 1", + isActive: true + } + ] + }; + + try { + spyApp.getPages.and.returnValue(Promise.resolve(testData.pages)); + const pages = await report.getPages(); + // Assert + expect(spyApp.getPages).toHaveBeenCalled(); + // Workaround to compare pages + pages.forEach(page => { + const testPage = util.find(p => p.name === page.name, testData.pages); + if (testPage) { + expect(page.name).toEqual(testPage.name); + expect(page.isActive).toEqual(testPage.isActive); + } + else { + expect(true).toBe(false); + } + }); + } catch (error) { + console.log("getPages failed with", error); + fail("getPages failed"); + } + }); + }); + + describe('filters', function () { + it('report.getFilters() returns promise that rejects with server error if there was problem getting filters', async function () { + // Arrange + const testData = { + expectedError: { + message: 'could not serialize filters' + } + }; + + try { + spyApp.getFilters.and.callFake(() => Promise.reject(testData.expectedError)); + await report.getFilters(); + fail("getFilters shouldn't succeed"); + } catch (error) { + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); + } + }); + + it('report.getFilters() returns promise that resolves with filters is request is successful', async function () { + // Arrange + const testData = { + filters: [ + { x: 'fakeFilter' } + ] + }; + + spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); + try { + + // Act + const filters = await report.getFilters(); + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + // @ts-ignore as testData is not of type IFilter + expect(filters).toEqual(testData.filters); + } catch (error) { + fail("get filtershousln't fails"); + } + }); + + it('report.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() + ], + expectedErrors: [ + { + message: 'invalid filter' + } + ] + }; + + spyApp.validateFilter.and.callFake(() => Promise.reject(testData.expectedErrors)); + try { + + // Act + await report.setFilters(testData.filters); + fail("et filter should fail"); + } catch (error) { + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).not.toHaveBeenCalled(); + expect(error).toEqual(jasmine.objectContaining(testData.expectedErrors)); + } + }); + + it('report.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', async function () { + // Arrange + const testData = { + filters: [(new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON()] + }; + + spyApp.validateFilter.and.returnValue(Promise.resolve(null)); + spyApp.setFilters.and.returnValue(Promise.resolve(null)); + try { + // Act + await report.setFilters(testData.filters); + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); + } catch (error) { + fail("why fail"); + } + }); + + it('report.removeFilters() returns promise that resolves with null if the request was accepted', async function () { + // Arrange + let spy = spyOn(report, 'updateFilters').and.callFake(() => Promise.resolve(null)); + try { + // Act + await report.removeFilters(); + // Assert + expect(spy).toHaveBeenCalledWith(models.FiltersOperations.RemoveAll); + } catch (error) { + fail("remove fialter shouldn't fail"); + } + }); + }); + + describe('print', function () { + it('report.print() returns promise that resolves with null if the report print command was accepted', async function () { + // Arrange + spyApp.print.and.returnValue(Promise.resolve({})); + // Act + const response = await report.print(); + // Assert + expect(spyApp.print).toHaveBeenCalled(); + expect(response).toEqual(); + }); + }); + + describe('refresh', function () { + it('report.refresh() returns promise that resolves with null if the report refresh command was accepted', async function () { + // Arrange + spyApp.refreshData.and.returnValue(Promise.resolve(null)); + // Act + const response = await report.refresh(); + // Assert + expect(spyApp.refreshData).toHaveBeenCalled(); + expect(response).toEqual(undefined); + }); + }); + + describe('settings', function () { + it('report.updateSettings(setting) returns promise that rejects with validation error if object is invalid', async function () { + // Arrange + const testData = { + settings: { + filterPaneEnabled: false + }, + expectedErrors: [ + { + message: 'invalid target' + } + ] + }; + spyApp.validateSettings.and.callFake(() => Promise.reject(testData.expectedErrors)); + + try { + // Act + await report.updateSettings(testData.settings); + fail("shouldfail"); + } catch (errors) { + // Assert + expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); + expect(spyApp.updateSettings).not.toHaveBeenCalled(); + expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors)); + } + }); + + it('report.updateSettings(settings) returns promise that resolves with null if requst is valid and accepted', async function () { + // Arrange + const testData = { + settings: { + filterPaneEnabled: false + }, + expectedErrors: [ + { + message: 'invalid target' + } + ] + }; + + try { + spyApp.validateSettings.and.returnValue(Promise.resolve(null)); + spyApp.updateSettings.and.returnValue(Promise.resolve(null)); + // Act + await report.updateSettings(testData.settings); + // Assert + expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); + expect(spyApp.updateSettings).toHaveBeenCalledWith(testData.settings); + } catch (error) { + console.log("updateSettings failed with", error); + fail("updateSettings failed"); + } + }); + }); + + describe('page', function () { + describe('filters', function () { + + beforeEach(() => { + spyApp.validatePage.and.returnValue(Promise.resolve(null)); + }); + + it('page.getFilters() returns promise that rejects with server error if there was problem getting filters', async function () { + // Arrange + const testData = { + expectedError: { + message: 'could not serialize filters' + } + }; + + try { + spyApp.getFilters.and.callFake(() => Promise.reject(testData.expectedError)); + + await page1.getFilters(); + } catch (error) { + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); + + } + }); + + it('page.getFilters() returns promise that resolves with filters is request is successful', async function () { + // Arrange + const testData = { + filters: [ + { x: 'fakeFilter' } + ] + }; + + try { + + spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); + const filters = await page1.getFilters(); + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + // @ts-ignore as testData is not of type IFilter as testData is not of type IFilter + expect(filters).toEqual(testData.filters); + } catch (error) { + fail("getFilters shouldn't fail"); + } + }); + + it('page.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', async function () { + // Arrange + const testData = { + filters: [ + (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() + ], + expectedErrors: [ + { + message: 'invalid filter' + } + ] + }; + + // await iframeLoaded; + try { + spyApp.validateFilter.and.callFake(() => Promise.reject(testData.expectedErrors)); + await page1.setFilters(testData.filters); + fail("setilters shouldn't fail"); + } catch (error) { + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).not.toHaveBeenCalled(); + expect(error).toEqual(jasmine.objectContaining(testData.expectedErrors)); + } + }); + + it('page.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', async function () { + // Arrange + const testData = { + filters: [(new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON()] + }; + + spyApp.validateFilter.and.returnValue(Promise.resolve(null)); + spyApp.setFilters.and.returnValue(Promise.resolve(null)); + try { + await page1.setFilters(testData.filters); + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); + } catch (error) { + console.log("setFilters failed with", error); + fail("setilters failed"); + } + }); + + it('page.removeFilters() returns promise that resolves with null if the request was accepted', async function () { + // Arrange + try { + spyApp.updateFilters.and.returnValue(Promise.resolve(null)); + // Act + await page1.removeFilters(); + } catch (error) { + console.log("removeFilters failed with", error); + fail("removeFilters failed"); + } + // Assert + expect(spyApp.updateFilters).toHaveBeenCalledWith(models.FiltersOperations.RemoveAll, undefined); + }); + + describe('setActive', function () { + it('page.setActive() returns promise that rejects if page is invalid', async function () { + // Arrange + const testData = { + errors: [ + { + message: 'page xyz was not found in report' + } + ] + }; + + spyApp.validatePage.and.callFake(() => Promise.reject(testData.errors)); + try { + // Act + await page1.setActive(); + fail("setActive shouldn't succeed"); + + } catch (errors) { + expect(spyApp.validatePage).toHaveBeenCalled(); + expect(spyApp.setPage).not.toHaveBeenCalled(); + expect(errors).toEqual(jasmine.objectContaining(testData.errors)); + } + spyApp.validatePage.and.callThrough(); + }); + + it('page.setActive() returns promise that resolves with null if request is successful', async function () { + // Act + spyApp.validatePage.and.returnValue(Promise.resolve(null)); + spyApp.setPage.and.returnValue(Promise.resolve(null)); + try { + await page1.setActive(); + expect(spyApp.validatePage).toHaveBeenCalled(); + expect(spyApp.setPage).toHaveBeenCalled(); + } catch (error) { + console.log("setActive failed with ", error); + fail("setActive failed"); + } + }); + }); + }); + }); + + describe('SDK-to-Router (Event subscription)', function () { + it(`report.on(eventName, handler) should throw error if eventName is not supported`, function () { + // Arrange + const testData = { + eventName: 'xyz', + handler: jasmine.createSpy('handler') + }; + + try { + report.on(testData.eventName, testData.handler); + fail("should throw exception"); + } catch (error) { + expect(1).toEqual(1); + } + }); + + it(`report.on(eventName, handler) should register handler and be called when POST /report/:uniqueId/events/:eventName is received`, async function () { + // Arrange + const testData = { + reportId: 'fakeReportId', + eventName: 'pageChanged', + handler: jasmine.createSpy('handler'), + simulatedPageChangeBody: { + initiator: 'sdk', + newPage: { + name: 'page1', + displayName: 'Page 1' + } + }, + expectedEvent: { + detail: { + initiator: 'sdk', + newPage: report.page('page1') + } + } + }; + + report.on(testData.eventName, testData.handler); + try { + // Act + await iframeHpm.post(`/reports/${report.config.uniqueId}/events/${testData.eventName}`, testData.simulatedPageChangeBody); + + } catch (error) { + fail("testshouldn't fail"); + } + // Assert + expect(testData.handler).toHaveBeenCalledWith(jasmine.any(CustomEvent)); + // Workaround to compare pages which prevents recursive loop in jasmine equals + // expect(testData.handler2).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedPageChangeBody })); + expect(testData.handler.calls.mostRecent().args[0].detail.newPage.name).toEqual(testData.expectedEvent.detail.newPage.name); + }); + + it(`if multiple reports with the same id are loaded into the host, and event occurs on one of them, only one report handler should be called`, async function () { + // Arrange + const testData = { + reportId: 'fakeReportId', + eventName: 'pageChanged', + handler: jasmine.createSpy('handler'), + handler2: jasmine.createSpy('handler2'), + simulatedPageChangeBody: { + initiator: 'sdk', + newPage: { + name: 'page1', + displayName: 'Page 1' + } + } + }; + + // Create a second iframe and report + const element2 = document.createElement('div'); + element2.id = "reportContainer2"; + element2.className = 'powerbi-report-container3'; + document.body.appendChild(element2); + const report2 = powerbi.embed(element2, embedConfiguration); + const iframe2 = element2.getElementsByTagName('iframe')[0]; + setupEmbedMockApp(iframe2.contentWindow, window, logMessages, 'SDK-to-MockApp IframeWpmp2'); + await new Promise((resolve, _reject) => { + iframe2.addEventListener('load', () => { + resolve(null); + }); + }); + + report.on(testData.eventName, testData.handler); + report2.on(testData.eventName, testData.handler2); + + try { + await iframeHpm.post(`/reports/${report2.config.uniqueId}/events/${testData.eventName}`, testData.simulatedPageChangeBody); + } catch (error) { + powerbi.reset(element2); + element2.remove(); + fail("hpm post shouldn't fail"); + } + // Act + expect(testData.handler).not.toHaveBeenCalled(); + expect(testData.handler2).toHaveBeenCalledWith(jasmine.any(CustomEvent)); + // Workaround to compare pages which prevents recursive loop in jasmine equals + // expect(testData.handler).toHaveBeenCalledWith(jasmine.objectContaining(testData.expectedEvent)); + expect(testData.handler2.calls.mostRecent().args[0].detail.newPage.name).toEqual(testData.simulatedPageChangeBody.newPage.name); + powerbi.reset(element2); + element2.remove(); + }); + + it(`ensure load event is allowed`, async function () { + // Arrange + const testData = { + reportId: 'fakeReportId', + eventName: 'loaded', + handler: jasmine.createSpy('handler3'), + simulatedBody: { + initiator: 'sdk' + } + }; + + report.on(testData.eventName, testData.handler); + + // Act + try { + await iframeHpm.post(`/reports/${report.config.uniqueId}/events/${testData.eventName}`, testData.simulatedBody); + } catch (error) { + fail("ensure load event is allowed failed"); + } + + // Assert + expect(testData.handler).toHaveBeenCalledWith(jasmine.any(CustomEvent)); + expect(testData.handler).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedBody })); + }); + }); + }); +}); diff --git a/test/SDK-to-WPMP.spec.ts b/test/SDK-to-WPMP.spec.ts new file mode 100644 index 00000000..4bf43146 --- /dev/null +++ b/test/SDK-to-WPMP.spec.ts @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as service from '../src/service'; +import * as report from '../src/report'; +import * as Wpmp from 'window-post-message-proxy'; +import * as factories from '../src/factories'; +import { spyWpmp } from './utility/mockWpmp'; +import { spyHpm } from './utility/mockHpm'; +import { spyRouter } from './utility/mockRouter'; +import { iframeSrc } from './constsants'; + +describe('SDK-to-WPMP', function () { + let element: HTMLDivElement; + let powerbi: service.Service; + let report: report.Report; + let uniqueId: string; + + beforeEach(function () { + const spyWpmpFactory: factories.IWpmpFactory = (_name?: string, _logMessages?: boolean) => { + return spyWpmp; + }; + + powerbi = new service.Service(factories.hpmFactory, spyWpmpFactory, factories.routerFactory); + + element = document.createElement('div'); + element.className = 'powerbi-report-container'; + + const embedConfiguration = { + type: "report", + id: "fakeReportId", + accessToken: 'fakeToken', + embedUrl: iframeSrc, + wpmpName: 'SDK-to-WPMP report wpmp' + }; + const hpmPostpy = spyOn(powerbi.hpm, "post").and.callFake(() => Promise.resolve({})); + report = powerbi.embed(element, embedConfiguration); + hpmPostpy.and.callThrough(); + uniqueId = report.config.uniqueId; + spyHpm.post.calls.reset(); + }); + + afterEach(function () { + powerbi.reset(element); + element.remove(); + + spyWpmp.stop(); + spyWpmp.addHandler.calls.reset(); + spyWpmp.clearHandlers(); + + spyHpm.get.calls.reset(); + spyHpm.post.calls.reset(); + spyHpm.patch.calls.reset(); + spyHpm.put.calls.reset(); + spyHpm.delete.calls.reset(); + + spyRouter.get.calls.reset(); + spyRouter.post.calls.reset(); + spyRouter.patch.calls.reset(); + spyRouter.put.calls.reset(); + spyRouter.delete.calls.reset(); + }); + + describe('Event handlers', function () { + it(`handler passed to report.on(eventName, handler) is called when POST /report/:uniqueId/events/:eventName is received`, function () { + // Arrange + const testData = { + eventName: 'filtersApplied', + handler: jasmine.createSpy('handler'), + filtersAppliedEvent: { + data: { + method: 'POST', + url: `/reports/${uniqueId}/events/filtersApplied`, + body: { + initiator: 'sdk', + filters: [ + { + x: 'fakeFilter' + } + ] + } + } + } + }; + + report.on(testData.eventName, testData.handler); + + // Act + spyWpmp.onMessageReceived(testData.filtersAppliedEvent); + + // Assert + expect(testData.handler).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.filtersAppliedEvent.data.body })); + }); + + it(`off('eventName', handler) will remove single handler which matches function reference for that event`, function () { + // Arrange + const testData = { + eventName: 'filtersApplied', + handler: jasmine.createSpy('handler1'), + simulatedEvent: { + data: { + method: 'POST', + url: `/reports/${uniqueId}/events/filtersApplied`, + body: { + initiator: 'sdk', + filter: { + x: '1', + y: '2' + } + } + } + } + }; + + report.on(testData.eventName, testData.handler); + report.off(testData.eventName, testData.handler); + + // Act + spyWpmp.onMessageReceived(testData.simulatedEvent); + + // Assert + expect(testData.handler).not.toHaveBeenCalled(); + }); + + it('if multiple handlers for the same event are registered they will all be called', function () { + // Arrange + const testData = { + eventName: 'filtersApplied', + handler: jasmine.createSpy('handler1'), + handler2: jasmine.createSpy('handler2'), + handler3: jasmine.createSpy('handler3'), + simulatedEvent: { + data: { + method: 'POST', + url: `/reports/${uniqueId}/events/filtersApplied`, + body: { + initiator: 'sdk', + filter: { + x: '1', + y: '2' + } + } + } + } + }; + + report.on(testData.eventName, testData.handler); + report.on(testData.eventName, testData.handler2); + report.on(testData.eventName, testData.handler3); + + // Act + spyWpmp.onMessageReceived(testData.simulatedEvent); + + // Assert + expect(testData.handler).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedEvent.data.body })); + expect(testData.handler2).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedEvent.data.body })); + expect(testData.handler3).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedEvent.data.body })); + }); + + it(`off('eventName') will remove all handlers which matches event name`, function () { + // Arrange + const testData = { + eventName: 'filtersApplied', + handler: jasmine.createSpy('handler1'), + handler2: jasmine.createSpy('handler2'), + handler3: jasmine.createSpy('handler3'), + simulatedEvent: { + data: { + method: 'POST', + url: '/reports/fakeReportId/events/filtersApplied', + body: { + initiator: 'sdk', + filter: { + x: '1', + y: '2' + } + } + } + } + }; + + report.on(testData.eventName, testData.handler); + report.on(testData.eventName, testData.handler2); + report.on(testData.eventName, testData.handler3); + report.off(testData.eventName); + + // Act + spyWpmp.onMessageReceived(testData.simulatedEvent); + + // Assert + expect(testData.handler).not.toHaveBeenCalled(); + expect(testData.handler2).not.toHaveBeenCalled(); + expect(testData.handler3).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/test/constsants.ts b/test/constsants.ts new file mode 100644 index 00000000..f944a5ea --- /dev/null +++ b/test/constsants.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +declare global { + interface Window { + __karma__: any; + } +} + +export const logMessages = (window.__karma__.config.args[0] === 'logMessages'); + +export const iframeSrc = "base/test/utility/noop.html"; +window.onbeforeunload = null; + diff --git a/test/filterBuilders.spec.ts b/test/filterBuilders.spec.ts index fbc88798..e7f2b432 100644 --- a/test/filterBuilders.spec.ts +++ b/test/filterBuilders.spec.ts @@ -439,7 +439,6 @@ describe('filter builders', function () { const includeToday = true; const relativeDateFilter: models.RelativeDateFilter = new models.RelativeDateFilter(target, operator, timeUnitsCount, timeUnitType, includeToday); - // Act const relativeDateFilterWithBuilder: models.RelativeDateFilter = new RelativeDateFilterBuilder() .withTargetObject(target) @@ -460,7 +459,6 @@ describe('filter builders', function () { const includeToday = true; const relativeDateFilter: models.RelativeDateFilter = new models.RelativeDateFilter(columnTarget, operator, timeUnitsCount, timeUnitType, includeToday); - // Act const relativeDateFilterWithBuilder: models.RelativeDateFilter = new RelativeDateFilterBuilder() .withColumnTarget('table', 'column') @@ -481,7 +479,6 @@ describe('filter builders', function () { const includeToday = true; const relativeDateFilter: models.RelativeDateFilter = new models.RelativeDateFilter(measureTarget, operator, timeUnitsCount, timeUnitType, includeToday); - // Act const relativeDateFilterWithBuilder: models.RelativeDateFilter = new RelativeDateFilterBuilder() .withMeasureTarget('table', 'measure') @@ -502,7 +499,6 @@ describe('filter builders', function () { const includeToday = true; const relativeDateFilter: models.RelativeDateFilter = new models.RelativeDateFilter(hierarchyLevelTarget, operator, timeUnitsCount, timeUnitType, includeToday); - // Act const relativeDateFilterWithBuilder: models.RelativeDateFilter = new RelativeDateFilterBuilder() .withHierarchyLevelTarget('table', 'hierarchy', 'hierarchy level name') @@ -523,7 +519,6 @@ describe('filter builders', function () { const includeToday = true; const relativeDateFilter: models.RelativeDateFilter = new models.RelativeDateFilter(columnAggregationTarget, operator, timeUnitsCount, timeUnitType, includeToday); - // Act const relativeDateFilterWithBuilder: models.RelativeDateFilter = new RelativeDateFilterBuilder() .withColumnAggregation('table', 'column', 'Avg') @@ -544,7 +539,6 @@ describe('filter builders', function () { const includeToday = true; const relativeDateFilter: models.RelativeDateFilter = new models.RelativeDateFilter(hierarchyLevelAggregationTarget, operator, timeUnitsCount, timeUnitType, includeToday); - // Act const relativeDateFilterWithBuilder: models.RelativeDateFilter = new RelativeDateFilterBuilder() .withHierarchyLevelAggregationTarget('table', 'hierarchy', 'hierarchy level name', 'Avg') @@ -567,7 +561,6 @@ describe('filter builders', function () { const timeUnitType = models.RelativeDateFilterTimeUnit.Hours; const relativeTimeFilter: models.RelativeTimeFilter = new models.RelativeTimeFilter(target, operator, timeUnitsCount, timeUnitType); - // Act const relativeTimeFilterWithBuilder: models.RelativeTimeFilter = new RelativeTimeFilterBuilder() .withTargetObject(target) @@ -586,7 +579,6 @@ describe('filter builders', function () { const timeUnitType = models.RelativeDateFilterTimeUnit.Hours; const relativeTimeFilter: models.RelativeTimeFilter = new models.RelativeTimeFilter(columnTarget, operator, timeUnitsCount, timeUnitType); - // Act const relativeTimeFilterWithBuilder: models.RelativeTimeFilter = new RelativeTimeFilterBuilder() .withColumnTarget('table', 'column') @@ -605,7 +597,6 @@ describe('filter builders', function () { const timeUnitType = models.RelativeDateFilterTimeUnit.Hours; const relativeTimeFilter: models.RelativeTimeFilter = new models.RelativeTimeFilter(measureTarget, operator, timeUnitsCount, timeUnitType); - // Act const relativeTimeFilterWithBuilder: models.RelativeTimeFilter = new RelativeTimeFilterBuilder() .withMeasureTarget('table', 'measure') @@ -624,7 +615,6 @@ describe('filter builders', function () { const timeUnitType = models.RelativeDateFilterTimeUnit.Hours; const relativeTimeFilter: models.RelativeTimeFilter = new models.RelativeTimeFilter(hierarchyLevelTarget, operator, timeUnitsCount, timeUnitType); - // Act const relativeTimeFilterWithBuilder: models.RelativeTimeFilter = new RelativeTimeFilterBuilder() .withHierarchyLevelTarget('table', 'hierarchy', 'hierarchy level name') @@ -643,7 +633,6 @@ describe('filter builders', function () { const timeUnitType = models.RelativeDateFilterTimeUnit.Hours; const relativeTimeFilter: models.RelativeTimeFilter = new models.RelativeTimeFilter(columnAggregationTarget, operator, timeUnitsCount, timeUnitType); - // Act const relativeTimeFilterWithBuilder: models.RelativeTimeFilter = new RelativeTimeFilterBuilder() .withColumnAggregation('table', 'column', 'Avg') @@ -662,7 +651,6 @@ describe('filter builders', function () { const timeUnitType = models.RelativeDateFilterTimeUnit.Hours; const relativeTimeFilter: models.RelativeTimeFilter = new models.RelativeTimeFilter(hierarchyLevelAggregationTarget, operator, timeUnitsCount, timeUnitType); - // Act const relativeTimeFilterWithBuilder: models.RelativeTimeFilter = new RelativeTimeFilterBuilder() .withHierarchyLevelAggregationTarget('table', 'hierarchy', 'hierarchy level name', 'Avg') @@ -674,4 +662,4 @@ describe('filter builders', function () { expect(relativeTimeFilterWithBuilder).toEqual(relativeTimeFilter); }); }); -}); \ No newline at end of file +}); diff --git a/test/protocol.spec.ts b/test/protocol.spec.ts new file mode 100644 index 00000000..9bf8e476 --- /dev/null +++ b/test/protocol.spec.ts @@ -0,0 +1,1194 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +import { WindowPostMessageProxy } from 'window-post-message-proxy'; +import { HttpPostMessage } from 'http-post-message'; +import { spyApp, setupEmbedMockApp } from './utility/mockEmbed'; +import { hpmFactory, routerFactory, wpmpFactory } from '../src/factories'; +import { iframeSrc, logMessages } from './constsants'; +import * as models from 'powerbi-models'; + +describe('Protocol', function () { + let hpm: HttpPostMessage; + let wpmp: WindowPostMessageProxy; + let iframe: HTMLIFrameElement; + let iframeHpm: HttpPostMessage; + + let spyHandler: { + test: jasmine.Spy; + handle: jasmine.Spy; + }; + + beforeEach(async function () { + iframe = document.createElement('iframe'); + iframe.id = 'protocol'; + iframe.src = iframeSrc; + document.body.appendChild(iframe); + + await new Promise(resolve => iframe.addEventListener('load', () => resolve(null))); + + // Register Iframe side + iframeHpm = setupEmbedMockApp(iframe.contentWindow, window, logMessages, 'ProtocolMockAppWpmp'); + + // Register SDK side WPMP + wpmp = wpmpFactory('HostProxyDefaultNoHandlers', logMessages, iframe.contentWindow); + hpm = hpmFactory(wpmp, iframe.contentWindow, 'testVersion'); + + const router = routerFactory(wpmp); + + spyHandler = { + test: jasmine.createSpy("testSpy").and.returnValue(true), + handle: jasmine.createSpy("handleSpy").and.callFake(function (message: any) { + message.handled = true; + return message; + }) + }; + + router.post('/reports/:uniqueId/events/:eventName', (req, res) => { + spyHandler.handle(req); + res.send(202); + }); + + router.post('/reports/:uniqueId/pages/:pageName/events/:eventName', (req, res) => { + spyHandler.handle(req); + res.send(202); + }); + + router.post('/reports/:uniqueId/pages/:pageName/visuals/:visualName/events/:eventName', (req, res) => { + spyHandler.handle(req); + res.send(202); + }); + }); + + afterEach(function () { + iframe.remove(); + wpmp.stop(); + spyHandler.test.calls.reset(); + spyHandler.handle.calls.reset(); + spyApp.reset(); + }); + + describe('HPM-to-MockApp', function () { + describe('notfound', function () { + it('GET request to uknown url returns 404 Not Found', async function () { + try { + await hpm.get('route/that/does/not/exist'); + } catch (response) { + expect(response.statusCode).toEqual(404); + } + }); + + it('POST request to uknown url returns 404 Not Found', async function () { + + try { + await hpm.post('route/that/does/not/exist', null); + } catch (response) { + expect(response.statusCode).toEqual(404); + } + }); + + it('PUT request to uknown url returns 404 Not Found', async function () { + try { + await hpm.put('route/that/does/not/exist', null); + } catch (response) { + expect(response.statusCode).toEqual(404); + } + }); + + it('PATCH request to uknown url returns 404 Not Found', async function () { + try { + await hpm.patch('route/that/does/not/exist', null); + } catch (response) { + expect(response.statusCode).toEqual(404); + } + }); + + it('DELETE request to uknown url returns 404 Not Found', async function () { + try { + await hpm.delete('route/that/does/not/exist'); + } catch (response) { + expect(response.statusCode).toEqual(404); + } + }); + }); + + describe('create', function () { + describe('report', function () { + it('POST /report/create returns 400 if the request is invalid', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + create: { + datasetId: "fakeId", + accessToken: "fakeToken", + } + }; + + spyApp.validateCreateReport.and.callFake(() => Promise.reject(null)); + + // Act + try { + await hpm.post('/report/create', testData.create, { uid: testData.uniqueId }); + fail("POST to /report/create should fail"); + } catch (response) { + // Assert + expect(spyApp.validateCreateReport).toHaveBeenCalledWith(testData.create); + expect(response.statusCode).toEqual(400); + } + }); + }); + + it('POST /report/create returns 202 if the request is valid', async function () { + // Arrange + const testData = { + create: { + datasetId: "fakeId", + accessToken: "fakeToken", + } + }; + + spyApp.validateCreateReport.and.returnValue(Promise.resolve(null)); + // Act + try { + const response = await hpm.post('/report/create', testData.create); + // Assert + expect(spyApp.validateCreateReport).toHaveBeenCalledWith(testData.create); + expect(response.statusCode).toEqual(202); + } catch (error) { + console.log("hpm.post failed with", error); + fail("hpm.post"); + } + }); + }); + }); + + describe('load & prepare', function () { + describe('report', function () { + for (let action of ['load', 'prepare']) { + it(`POST /report/${action} returns 400 if the request is invalid`, async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + load: { + reportId: "fakeId", + accessToken: "fakeToken", + options: { + } + } + }; + + spyApp.validateReportLoad.and.callFake(() => Promise.reject(null)); + + // Act + try { + await hpm.post(`/report/${action}`, testData.load, { uid: testData.uniqueId }); + fail(`Post to /report/${action} should fail`); + } catch (response) { + // Assert + expect(spyApp.validateReportLoad).toHaveBeenCalledWith(testData.load); + expect(spyApp.reportLoad).not.toHaveBeenCalledWith(testData.load); + expect(response.statusCode).toEqual(400); + } + }); + + it(`POST /report/${action} returns 202 if the request is valid`, async function () { + // Arrange + const testData = { + load: { + reportId: "fakeId", + accessToken: "fakeToken", + options: { + } + } + }; + + spyApp.validateReportLoad.and.callFake(() => Promise.resolve({})); + try { + const response = await hpm.post(`/report/${action}`, testData.load); + // Assert + expect(spyApp.validateReportLoad).toHaveBeenCalledWith(testData.load); + expect(spyApp.reportLoad).toHaveBeenCalledWith(testData.load); + expect(response.statusCode).toEqual(202); + } catch (error) { + console.error(error); + fail(error); + } + }); + + it(`POST /report/${action} causes POST /reports/:uniqueId/events/loaded`, async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + load: { + reportId: "fakeId", + accessToken: "fakeToken", + options: { + navContentPaneEnabled: false + } + }, + }; + const testExpectedEvent = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/loaded`, + body: { + initiator: 'sdk' + } + }; + + try { + // Act + await hpm.post(`/report/${action}`, testData.load, { uid: testData.uniqueId }); + // Assert + expect(spyApp.validateReportLoad).toHaveBeenCalledWith(testData.load); + expect(spyApp.reportLoad).toHaveBeenCalledWith(testData.load); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); + } catch (error) { + console.log("hpm.post failed with", error); + fail("hpm.post failed"); + } + }); + + it(`POST /report/${action} causes POST /reports/:uniqueId/events/error`, async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + load: { + reportId: "fakeId", + accessToken: "fakeToken", + options: { + navContentPaneEnabled: false + } + }, + error: { + message: "error message" + } + }; + const testExpectedEvent = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/error`, + body: testData.error + }; + + spyApp.reportLoad.and.callFake(() => Promise.reject(testData.error)); + try { + // Act + await hpm.post(`/report/${action}`, testData.load, { uid: testData.uniqueId }); + expect(spyApp.validateReportLoad).toHaveBeenCalledWith(testData.load); + expect(spyApp.reportLoad).toHaveBeenCalledWith(testData.load); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); + } catch (error) { + console.log("hpm pody failed with", error); + fail("hpm pody failed"); + } + }); + } + }); + + describe('dashboard', function () { + it('POST /dashboard/load returns 202 if the request is valid', async function () { + + // Arrange + const testData = { + load: { + dashboardId: "fakeId", + accessToken: "fakeToken", + options: { + } + } + }; + + spyApp.validateDashboardLoad.and.returnValue(Promise.resolve(null)); + try { + // Act + const response = await hpm.post('/dashboard/load', testData.load); + // Assert + expect(spyApp.validateDashboardLoad).toHaveBeenCalledWith(testData.load); + expect(spyApp.dashboardLoad).toHaveBeenCalledWith(testData.load); + expect(response.statusCode).toEqual(202); + } catch (error) { + + console.error(error); + fail(error); + } + }); + + it('POST /dashboard/load returns 400 if the request is invalid', async function () { + + // Arrange + const testData = { + uniqueId: 'uniqueId', + load: { + dashboardId: "fakeId", + accessToken: "fakeToken", + options: { + } + } + }; + + spyApp.validateDashboardLoad.and.callFake(() => Promise.reject(null)); + + try { + // Act + await hpm.post('/dashboard/load', testData.load, { uid: testData.uniqueId }); + fail("POST to /dashboard/load should fail"); + } catch (response) { + // Assert + expect(spyApp.validateDashboardLoad).toHaveBeenCalledWith(testData.load); + expect(spyApp.dashboardLoad).not.toHaveBeenCalledWith(testData.load); + expect(response.statusCode).toEqual(400); + } + }); + }); + }); + + describe('render', function () { + it('POST /report/render returns 202 if the request is valid', async function () { + // Arrange + spyApp.render.and.returnValue(Promise.resolve(null)); + // Act + const response = await hpm.post('/report/render', null); + // Assert + expect(spyApp.render).toHaveBeenCalled(); + expect(response.statusCode).toEqual(202); + }); + }); + + describe('pages', function () { + it('GET /report/pages returns 200 with body as array of pages', async function () { + // Arrange + const testData = { + expectedPages: [ + { + name: "a" + }, + { + name: "b" + } + ] + }; + + spyApp.getPages.and.returnValue(Promise.resolve(testData.expectedPages)); + // Act + const response = await hpm.get('/report/pages'); + // Assert + expect(spyApp.getPages).toHaveBeenCalled(); + const pages = response.body; + // @ts-ignore as testData is not of type IFilter + expect(pages).toEqual(testData.expectedPages); + }); + + it('GET /report/pages returns 500 with body as error', async function () { + // Arrange + const testData = { + expectedError: { + message: "could not query pages" + } + }; + + spyApp.getPages.and.callFake(() => Promise.reject(testData.expectedError)); + try { + // Act + await hpm.get('/report/pages'); + fail("Get /report/pages should fail"); + } catch (response) { + // Assert + expect(spyApp.getPages).toHaveBeenCalled(); + const error = response.body; + expect(error).toEqual(testData.expectedError); + } + }); + + it('PUT /report/pages/active returns 400 if request is invalid', async function () { + // Arrange + const testData = { + page: { + name: "fakeName" + } + }; + + spyApp.validatePage.and.callFake(() => Promise.reject(null)); + try { + + // Act + await hpm.put('/report/pages/active', testData.page); + fail("put to /report/pages/active should fail"); + } catch (response) { + // Assert + expect(spyApp.validatePage).toHaveBeenCalledWith(testData.page); + expect(spyApp.setPage).not.toHaveBeenCalled(); + expect(response.statusCode).toEqual(400); + } + }); + + it('PUT /report/pages/active returns 202 if request is valid', async function () { + // Arrange + const testData = { + page: { + name: "fakeName" + } + }; + + spyApp.validatePage.and.returnValue(Promise.resolve(null)); + + // Act + const response = await hpm.put('/report/pages/active', testData.page); + // Assert + expect(spyApp.validatePage).toHaveBeenCalledWith(testData.page); + expect(spyApp.setPage).toHaveBeenCalledWith(testData.page); + expect(response.statusCode).toEqual(202); + }); + + it('PUT /report/pages/active causes POST /reports/:uniqueId/events/pageChanged', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + page: { + name: "fakeName" + } + }; + const expectedEvent = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/pageChanged`, + body: jasmine.objectContaining({ + initiator: 'sdk' + }) + }; + + spyApp.validatePage.and.returnValue(Promise.resolve(null)); + spyHandler.handle.calls.reset(); + // Act + const response = await hpm.put('/report/pages/active', testData.page, { uid: testData.uniqueId }); + // Assert + expect(spyApp.validatePage).toHaveBeenCalledWith(testData.page); + expect(spyApp.setPage).toHaveBeenCalledWith(testData.page); + expect(response.statusCode).toEqual(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(expectedEvent)); + }); + + it('PUT /report/pages/active causes POST /reports/:uniqueId/events/error', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + page: { + name: "fakeName" + }, + error: { + message: "error" + } + }; + const expectedEvent = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/error`, + body: testData.error + }; + + spyApp.validatePage.and.returnValue(Promise.resolve(null)); + spyApp.setPage.and.callFake(() => Promise.reject(testData.error)); + + // Act + const response = await hpm.put('/report/pages/active', testData.page, { uid: testData.uniqueId }); + // Assert + expect(spyApp.validatePage).toHaveBeenCalledWith(testData.page); + expect(spyApp.setPage).toHaveBeenCalledWith(testData.page); + expect(response.statusCode).toEqual(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(expectedEvent)); + }); + + describe('refresh', function () { + it('POST /report/refresh returns 202 if the request is valid', async function () { + // Arrange + spyApp.refreshData.and.returnValue(Promise.resolve(null)); + // Act + const response = await hpm.post('/report/refresh', null); + // Assert + expect(spyApp.refreshData).toHaveBeenCalled(); + expect(response.statusCode).toEqual(202); + + }); + }); + + describe('print', function () { + it('POST /report/print returns 202 if the request is valid', async function () { + // Arrange + spyApp.print.and.returnValue(Promise.resolve(null)); + // Act + const response = await hpm.post('/report/print', null); + // Assert + expect(spyApp.print).toHaveBeenCalled(); + expect(response.statusCode).toEqual(202); + + }); + }); + + describe('switchMode', function () { + it('POST /report/switchMode returns 202 if the request is valid', async function () { + // Arrange + spyApp.switchMode.and.returnValue(Promise.resolve(null)); + // Act + const response = await hpm.post('/report/switchMode/Edit', null); + // Assert + expect(spyApp.switchMode).toHaveBeenCalled(); + expect(response.statusCode).toEqual(202); + }); + }); + + describe('save', function () { + it('POST /report/save returns 202 if the request is valid', async function () { + // Arrange + spyApp.save.and.returnValue(Promise.resolve(null)); + // Act + const response = await hpm.post('/report/save', null); + // Assert + expect(spyApp.save).toHaveBeenCalled(); + expect(response.statusCode).toEqual(202); + }); + }); + }); + + describe('saveAs', function () { + it('POST /report/saveAs returns 202 if the request is valid', async function () { + // Arrange + let saveAsParameters: models.ISaveAsParameters = { name: "reportName" }; + + spyApp.saveAs.and.returnValue(Promise.resolve(null)); + // Act + const response = await hpm.post('/report/saveAs', saveAsParameters); + // Assert + expect(spyApp.saveAs).toHaveBeenCalled(); + expect(spyApp.saveAs).toHaveBeenCalledWith(saveAsParameters); + expect(response.statusCode).toEqual(202); + }); + }); + + describe('setAccessToken', function () { + it('POST /report/token returns 202 if the request is valid', async function () { + // Arrange + let accessToken: string = "fakeToken"; + + spyApp.setAccessToken.and.returnValue(Promise.resolve(null)); + // Act + const response = await hpm.post('/report/token', accessToken); + // Assert + expect(spyApp.setAccessToken).toHaveBeenCalled(); + expect(spyApp.setAccessToken).toHaveBeenCalledWith(accessToken); + expect(response.statusCode).toEqual(202); + }); + }); + + describe('filters (report level)', function () { + it('GET /report/filters returns 200 with body as array of filters', async function () { + // Arrange + const testData = { + filters: [ + { + name: "fakeFilter1" + }, + { + name: "fakeFilter2" + } + ] + }; + + spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); + + // Act + const response = await hpm.get('/report/filters'); + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + expect(response.statusCode).toEqual(200); + // @ts-ignore as testData is not of type IFilter + expect(response.body).toEqual(testData.filters); + }); + + it('GET /report/filters returns 500 with body as error', async function () { + // Arrange + const testData = { + error: { + message: "internal error" + } + }; + + spyApp.getFilters.and.callFake(() => Promise.reject(testData.error)); + + // Act + try { + await hpm.get('/report/filters'); + + } catch (response) { + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + expect(response.statusCode).toEqual(500); + expect(response.body).toEqual(testData.error); + } + }); + + it('PUT /report/filters returns 400 if request is invalid', async function () { + // Arrange + const testData = { + filters: [ + { + name: "fakeFilter" + } + ] + }; + + spyApp.validateFilter.and.callFake(() => Promise.reject(null)); + + // Act + try { + await hpm.put('/report/filters', testData.filters); + + } catch (response) { + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).not.toHaveBeenCalled(); + expect(response.statusCode).toEqual(400); + } + }); + + it('PUT /report/filters returns 202 if request is valid', async function () { + // Arrange + const testData = { + filters: [ + { + name: "fakeFilter" + } + ] + }; + + spyApp.validateFilter.and.returnValue(Promise.resolve(null)); + + // Act + const response = await hpm.put('/report/filters', testData.filters); + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); + expect(response.statusCode).toEqual(202); + }); + + it('PUT /report/filters will cause POST /reports/:uniqueId/events/filtersApplied', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + filters: [ + { + name: "fakeFilter" + } + ] + }; + const testExpectedEvent = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/filtersApplied` + }; + + spyApp.validateFilter.and.returnValue(Promise.resolve(null)); + + // Act + const response = await hpm.put('/report/filters', testData.filters, { uid: testData.uniqueId }); + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); + expect(response.statusCode).toEqual(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); + }); + }); + + describe('filters (page level)', function () { + beforeEach(() => { + spyApp.validatePage.and.returnValue(Promise.resolve(null)); + spyApp.validateFilter.and.returnValue(Promise.resolve(null)); + }); + it('GET /report/pages/xyz/filters returns 200 with body as array of filters', async function () { + // Arrange + const testData = { + filters: [ + { + name: "fakeFilter1" + }, + { + name: "fakeFilter2" + } + ] + }; + + spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); + + // Act + const response = await hpm.get('/report/pages/xyz/filters'); + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + expect(response.statusCode).toEqual(200); + // @ts-ignore as testData is not of type IFilter + expect(response.body).toEqual(testData.filters); + }); + + it('GET /report/pages/xyz/filters returns 500 with body as error', async function () { + // Arrange + const testData = { + error: { + message: "internal error" + } + }; + + spyApp.getFilters.and.callFake(() => Promise.reject(testData.error)); + + // Act + try { + await hpm.get('/report/pages/xyz/filters'); + + } catch (response) { + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + expect(response.statusCode).toEqual(500); + expect(response.body).toEqual(testData.error); + + } + }); + + it('PUT /report/pages/xyz/filters returns 400 if request is invalid', async function () { + // Arrange + const testData = { + filters: [ + { + name: "fakeFilter" + } + ] + }; + + spyApp.validateFilter.and.callFake(() => Promise.reject(null)); + + // Act + try { + await hpm.put('/report/pages/xyz/filters', testData.filters); + + } catch (response) { + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).not.toHaveBeenCalled(); + expect(response.statusCode).toEqual(400); + } + }); + + it('PUT /report/pages/xyz/filters returns 202 if request is valid', async function () { + // Arrange + const testData = { + filters: [ + { + name: "fakeFilter" + } + ], + }; + + spyApp.validateFilter.and.returnValue(Promise.resolve(null)); + + // Act + const response = await hpm.put('/report/pages/xyz/filters', testData.filters); + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); + expect(response.statusCode).toEqual(202); + }); + + it('PUT /report/pages/xyz/filters will cause POST /reports/:uniqueId/pages/xyz/events/filtersApplied', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + filters: [ + { + name: "fakeFilter" + } + ] + }; + const testExpectedEvent = { + method: 'POST', + url: `/reports/${testData.uniqueId}/pages/xyz/events/filtersApplied` + }; + + spyApp.validateFilter.and.returnValue(Promise.resolve(null)); + + // Act + const response = await hpm.put('/report/pages/xyz/filters', testData.filters, { uid: testData.uniqueId }); + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); + expect(response.statusCode).toEqual(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); + }); + }); + + describe('filters (visual level)', function () { + it('GET /report/pages/xyz/visuals/uvw/filters returns 200 with body as array of filters', async function () { + // Arrange + const testData = { + filters: [ + { + name: "fakeFilter1" + }, + { + name: "fakeFilter2" + } + ] + }; + + spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); + + // Act + try { + + const response = await hpm.get('/report/pages/xyz/visuals/uvw/filters'); + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + expect(response.statusCode).toEqual(200); + // @ts-ignore as testData is not of type IFilter + expect(response.body).toEqual(testData.filters); + } catch (error) { + console.log("get filter shouldn't fail"); + } + }); + + it('GET /report/pages/xyz/visuals/uvw/filters returns 500 with body as error', async function () { + // Arrange + const testData = { + error: { + message: "internal error" + } + }; + + spyApp.getFilters.and.callFake(() => Promise.reject(testData.error)); + + // Act + try { + await hpm.get('/report/pages/xyz/visuals/uvw/filters'); + + } catch (response) { + + // Assert + expect(spyApp.getFilters).toHaveBeenCalled(); + expect(response.statusCode).toEqual(500); + expect(response.body).toEqual(testData.error); + + } + }); + + it('PUT /report/pages/xyz/visuals/uvw/filters returns 400 if request is invalid', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + filters: [ + { + name: "fakeFilter" + } + ] + }; + + spyApp.validateFilter.and.callFake(() => Promise.reject(null)); + + // Act + try { + await hpm.put('/report/pages/xyz/visuals/uvw/filters', testData.filters, { uid: testData.uniqueId }); + + } catch (response) { + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).not.toHaveBeenCalled(); + expect(response.statusCode).toEqual(400); + + } + }); + + it('PUT /report/pages/xyz/visuals/uvw/filters returns 202 if request is valid', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + filters: [ + { + name: "fakeFilter" + } + ], + }; + + spyApp.validateFilter.and.returnValue(Promise.resolve(null)); + + // Act + const response = await hpm.put('/report/pages/xyz/visuals/uvw/filters', testData.filters, { uid: testData.uniqueId }); + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); + expect(response.statusCode).toEqual(202); + }); + + it('PUT /report/:uniqueId/pages/xyz/visuals/uvw/filters will cause POST /reports/:uniqueId/pages/xyz/visuals/uvw/events/filtersApplied', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + filters: [ + { + name: "fakeFilter" + } + ] + }; + const testExpectedEvent = { + method: 'POST', + url: `/reports/${testData.uniqueId}/pages/xyz/visuals/uvw/events/filtersApplied` + }; + // Act + try { + const response = await hpm.put('/report/pages/xyz/visuals/uvw/filters', testData.filters, { uid: testData.uniqueId }); + // Assert + expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); + expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); + expect(response.statusCode).toEqual(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); + } catch (error) { + console.log("hpm.put failed with", error); + fail("hpm.put failed"); + } + }); + }); + + describe('settings', function () { + it('PATCH /report/settings returns 400 if request is invalid', async function () { + // Arrange + const testData = { + settings: { + filterPaneEnabled: false, + navContentPaneEnabled: false + } + }; + + spyApp.validateSettings.and.callFake(() => Promise.reject(null)); + + // Act + try { + await hpm.patch('/report/settings', testData.settings); + + } catch (response) { + // Assert + expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); + expect(spyApp.updateSettings).not.toHaveBeenCalled(); + expect(response.statusCode).toEqual(400); + } + }); + + it('PATCH /report/settings returns 202 if request is valid', async function () { + // Arrange + const testData = { + settings: { + filterPaneEnabled: false, + navContentPaneEnabled: false + } + }; + + spyApp.validateSettings.and.returnValue(Promise.resolve(null)); + + // Act + const response = await hpm.patch('/report/settings', testData.settings); + // Assert + expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); + expect(spyApp.updateSettings).toHaveBeenCalledWith(testData.settings); + expect(response.statusCode).toEqual(202); + }); + + it('PATCH /report/settings causes POST /reports/:uniqueId/events/settingsUpdated', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + settings: { + filterPaneEnabled: false + } + }; + const testExpectedEvent = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/settingsUpdated`, + body: { + initiator: 'sdk', + settings: { + filterPaneEnabled: false, + navContentPaneEnabled: false + } + } + }; + + spyApp.validateSettings.and.returnValue(Promise.resolve(null)); + spyApp.updateSettings.and.returnValue(Promise.resolve(testExpectedEvent.body.settings)); + + // Act + const response = await hpm.patch('/report/settings', testData.settings, { uid: testData.uniqueId }); + // Assert + expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); + expect(spyApp.updateSettings).toHaveBeenCalledWith(testData.settings); + expect(response.statusCode).toEqual(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); + }); + }); + + describe('MockApp-to-HPM', function () { + describe('pages', function () { + it('POST /reports/:uniqueId/events/pageChanged when user changes page', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + event: { + initiator: 'user', + newPage: { + name: "fakePageName" + } + } + }; + const testExpectedRequest = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/pageChanged`, + body: testData.event + }; + // Act + const response = await iframeHpm.post(testExpectedRequest.url, testData.event); + // Assert + expect(response.statusCode).toBe(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); + + }); + }); + + describe('filters (report level)', function () { + it('POST /reports/:uniqueId/events/filtersApplied when user changes filter', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + event: { + initiator: 'user', + filters: [ + { + name: "fakeFilter" + } + ] + } + }; + const testExpectedRequest = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/filtersApplied`, + body: testData.event + }; + // Act + const response = await iframeHpm.post(testExpectedRequest.url, testData.event); + // Assert + expect(response.statusCode).toBe(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); + }); + + describe('filters (page level)', function () { + it('POST /reports/:uniqueId/pages/xyz/events/filtersApplied when user changes filter', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + event: { + initiator: 'user', + filters: [ + { + name: "fakeFilter" + } + ] + } + }; + const testExpectedRequest = { + method: 'POST', + url: `/reports/${testData.uniqueId}/pages/xyz/events/filtersApplied`, + body: testData.event + }; + // Act + const response = await iframeHpm.post(testExpectedRequest.url, testData.event); + // Assert + expect(response.statusCode).toBe(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); + }); + }); + + describe('filters (visual level)', function () { + it('POST /reports/:uniqueId/pages/xyz/visuals/uvw/events/filtersApplied when user changes filter', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + event: { + initiator: 'user', + filters: [ + { + name: "fakeFilter" + } + ] + } + }; + const testExpectedRequest = { + method: 'POST', + url: `/reports/${testData.uniqueId}/pages/xyz/visuals/uvw/events/filtersApplied`, + body: testData.event + }; + + // Act + const response = await iframeHpm.post(testExpectedRequest.url, testData.event); + // Assert + expect(response.statusCode).toBe(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); + }); + }); + + describe('settings', function () { + it('POST /reports/:uniqueId/events/settingsUpdated when user changes settings', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + event: { + initiator: 'user', + settings: { + navContentPaneEnabled: true + } + } + }; + const testExpectedRequest = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/settingsUpdated`, + body: testData.event + }; + // Act + const response = await iframeHpm.post(testExpectedRequest.url, testData.event); + // Assert + expect(response.statusCode).toBe(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); + }); + }); + + describe('data selection', function () { + it('POST /reports/:uniqueId/events/dataSelected when user selects data', async function () { + // Arrange + const testData = { + uniqueId: 'uniqueId', + reportId: 'fakeReportId', + event: { + initiator: 'user', + selection: { + data: true + } + } + }; + const testExpectedRequest = { + method: 'POST', + url: `/reports/${testData.uniqueId}/events/dataSelected`, + body: testData.event + }; + // Act + const response = await iframeHpm.post(testExpectedRequest.url, testData.event); + // Assert + expect(response.statusCode).toBe(202); + expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); + }); + }); + }); + }); +}); diff --git a/test/service.spec.ts b/test/service.spec.ts new file mode 100644 index 00000000..afb81dc7 --- /dev/null +++ b/test/service.spec.ts @@ -0,0 +1,1044 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import * as service from '../src/service'; +import * as embed from '../src/embed'; +import * as report from '../src/report'; +import * as create from '../src/create'; +import * as factories from '../src/factories'; +import { EmbedUrlNotSupported } from '../src/errors'; + +// Todo: remove JQuery usage from this tests file. + +function ValidateDashboardConfigurationWorksAsExpected(pageView: string, exceptionExpected: boolean, powerbi: service.Service): void { + const embedUrl = `https://app.powerbi.com/dashboardEmbed`; + const component = $(`
`) + .appendTo('#powerbi-fixture'); + + const dashboardEmbedConfig = { + type: "dashboard", + id: "fakeReportId", + groupId: "fakeGroupId", + accessToken: "fakeAccessToken", + embedUrl: "fakeEmbedUrl", + pageView: pageView + }; + + let exceptionThrown = false; + // Act + try { + powerbi.embed(component[0], dashboardEmbedConfig); + } + catch (e) { + exceptionThrown = true; + } + + // Assert + expect(exceptionThrown).toBe(exceptionExpected); +} + +describe('service', function () { + let powerbi: service.Service; + let element: HTMLDivElement; + + beforeEach(function () { + powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory); + powerbi.accessToken = 'ABC123'; + element = document.createElement('div'); + element.id = 'powerbi-fixture'; + document.body.appendChild(element); + }); + + afterEach(function () { + element.remove(); + powerbi.wpmp.stop(); + }); + + it('is defined', function () { + expect(powerbi).toBeDefined(); + }); + + describe('init', function () { + it('embeds all components found in the DOM', function () { + // Arrange + const elements = [ + '
', + '
', + ]; + + elements.forEach(element => { + $(element).appendTo('#powerbi-fixture'); + }); + + // Act + powerbi.init(); + + // Assert + // If embed element has iframe inside it, assume embed action occurred + const iframes = document.querySelectorAll('[powerbi-embed-url] iframe'); + expect(iframes.length).toEqual(2); + }); + + it('embeds all components found in the DOM without id attribute', function () { + // Arrange + const elements = [ + '
', + '
', + ]; + + elements.forEach(element => { + $(element).appendTo('#powerbi-fixture'); + }); + + // Act + powerbi.init(); + + // Assert + // If embed element has iframe inside it, assume embed action occurred + const iframes = document.querySelectorAll('[powerbi-embed-url] iframe'); + expect(iframes.length).toEqual(2); + }); + + it('embeds all components found in the DOM with duplicate id attribute', function () { + // Arrange + const elements = [ + '
', + '
', + ]; + + elements.forEach(element => { + $(element).appendTo('#powerbi-fixture'); + }); + + // Act + powerbi.init(); + + // Assert + // If embed element has iframe inside it, assume embed action occurred + const iframes = document.querySelectorAll('[powerbi-embed-url] iframe'); + expect(iframes.length).toEqual(2); + }); + }); + + describe('get', function () { + it('if attempting to get a powerbi component on an element which was not embedded, throw an error', function () { + // Arrange + const $component = $('
'); + + // Act + const attemptGet = (): void => { + powerbi.get($component[0]); + }; + + // Assert + expect(attemptGet).toThrowError(Error); + }); + + it('calling get on element with embeded report component returns the instance', function () { + // Arrange + const $element = $('
') + .appendTo('#powerbi-fixture'); + + const componentInstance = powerbi.embed($element[0]); + + // Act + const componentInstance2 = powerbi.get($element[0]); + + // Assert + expect(componentInstance).toEqual(componentInstance2); + }); + + it('calling get on element with embeded dashboard component returns the instance', function () { + // Arrange + const $element = $('
') + .appendTo('#powerbi-fixture'); + + const componentInstance = powerbi.embed($element[0]); + + // Act + const componentInstance2 = powerbi.get($element[0]); + + // Assert + expect(componentInstance).toEqual(componentInstance2); + }); + }); + + describe('embed', function () { + it('if attempting to embed without specifying a type, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + // Act + const attemptEmbed = (): void => { + powerbi.embed(component[0]); + }; + + // Assert + expect(attemptEmbed).toThrowError(Error); + }); + + it('if attempting to embed with an unknown type, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + // Act + const attemptEmbed = (): void => { + powerbi.embed(component[0]); + }; + + // Assert + expect(attemptEmbed).toThrowError(Error); + }); + + it('if attempting to embed on existing element with different type than previous embed, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + const reportEmbedConfig: embed.IEmbedConfiguration = { + type: "report", + id: "fakeReportId", + accessToken: "fakeAccessToken", + embedUrl: "fakeEmbedUrl", + groupId: "fakeGroupId", + }; + + const dashboardEmbedConfig: embed.IEmbedConfiguration = { + type: "dashboard", + id: "fakeDashboardId", + accessToken: "fakeAccessToken", + embedUrl: "fakeEmbedUrl", + groupId: "fakeGroupId" + }; + + powerbi.embed(component[0], reportEmbedConfig); + + // Act + const attemptEmbed = (): void => { + powerbi.embed(component[0], dashboardEmbedConfig); + }; + + // Assert + expect(attemptEmbed).toThrowError(Error); + }); + + it('if Create is already embedded in element re-use the existing component by calling load with the new information', function () { + // Arrange + const $element = $('
') + .appendTo('#powerbi-fixture'); + + const testConfiguration = { + accessToken: "fakeAccessToken", + embedUrl: 'fakeUrl', + id: 'report2', + type: 'report', + groupId: "fakeGroupId" + }; + + const createConfig: embed.IEmbedConfiguration = { + datasetId: "fakeDashboardId", + accessToken: "fakeAccessToken", + embedUrl: "fakeEmbedUrl", + groupId: "fakeGroupId" + }; + + // Act + const component = powerbi.createReport($element[0], createConfig); + const component2 = powerbi.embed($element[0], testConfiguration); + const component3 = powerbi.get($element[0]); + + // Assert + expect(component).toBeDefined(); + expect(component2).toBeDefined(); + expect(component2).toBe(component3); + }); + + it('Create embed url with correct locale parameters', function () { + // Arrange + const $reportContainer = $('
') + .appendTo('#powerbi-fixture'); + + const testConfiguration: embed.IEmbedConfiguration = { + accessToken: "fakeAccessToken", + embedUrl: 'fakeUrl?reportId=1', + id: 'report2', + type: 'report', + settings: { + localeSettings: { + language: 'languageName', + formatLocale: 'formatName' + } + }, + groupId: "fakeGroupId", + uniqueId: "fakeUid", + }; + + powerbi.embed($reportContainer[0], testConfiguration); + let iframe = $reportContainer.find('iframe'); + expect(iframe.attr('src')).toEqual('fakeUrl?reportId=1&language=languageName&formatLocale=formatName&uid=fakeUid'); + }); + + it('if attempting to embed without specifying an embed url, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + // Act + const attemptEmbed = (): void => { + const configuration: embed.IEmbedConfiguration = { type: "report", embedUrl: null, accessToken: null, id: null }; + powerbi.embed(component[0], configuration); + }; + + // Assert + expect(attemptEmbed).toThrowError(Error); + }); + + it('if attempting to embed without specifying an access token, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + const originalToken = powerbi.accessToken; + powerbi.accessToken = undefined; + + // Act + const attemptEmbed = (): void => { + const configuration: embed.IEmbedConfiguration = { type: "report", embedUrl: null, accessToken: null, id: null }; + powerbi.embed(component[0], configuration); + }; + + // Assert + expect(attemptEmbed).toThrowError(Error); + + // Cleanup + powerbi.accessToken = originalToken; + }); + + it('if attempting to embed without specifying an id, throw error', function () { + // Arrange + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const attemptToEmbed = (): void => { + powerbi.embed($reportContainer[0]); + }; + + // Assert + expect(attemptToEmbed).toThrowError(); + }); + + it('if attempting to embed a dashboard with an invalid pageView, throw error', function () { + ValidateDashboardConfigurationWorksAsExpected("notValid", true, powerbi); + }); + + it('if attempting to embed a dashboard with a pageView equals fitToWidth, don\'t throw error', function () { + ValidateDashboardConfigurationWorksAsExpected("fitToWidth", false, powerbi); + }); + + it('if attempting to embed a dashboard with a pageView equals oneColumn, don\'t throw error', function () { + ValidateDashboardConfigurationWorksAsExpected("oneColumn", false, powerbi); + }); + + it('if attempting to embed a dashboard with a pageView equals actualSize, don\'t throw error', function () { + ValidateDashboardConfigurationWorksAsExpected("actualSize", false, powerbi); + }); + + it('if attempting to embed a dashboard with an undefined pageView, don\'t throw error', function () { + ValidateDashboardConfigurationWorksAsExpected(undefined, false, powerbi); + }); + + it('should get uqiqueId from config first', function () { + // Arrange + const testUniqueId = 'fakeUniqueId'; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.embed($reportContainer[0], { uniqueId: testUniqueId }); + + // Assert + expect(report.config.uniqueId).toEqual(testUniqueId); + }); + + it('should get uqiqueId from name attribute if uniqueId is not specified in config', function () { + // Arrange + const testUniqueId = 'fakeUniqueId'; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.embed($reportContainer[0]); + + // Assert + expect(report.config.uniqueId).toEqual(testUniqueId); + }); + + it('should generate uqiqueId if uniqueId is not specified in config or attribute', function () { + // Arrange + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.embed($reportContainer[0]); + + // Assert + expect(report.config.uniqueId).toEqual(jasmine.any(String)); + }); + + it('should get group id from configuration first', function () { + // Arrange + const testGroupId = "ABC123"; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?groupId=DIFFERENTID`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + const configuration: embed.IEmbedConfiguration = { id: 'fakeId', groupId: testGroupId }; + + // Act + const report = powerbi.embed($reportContainer[0], configuration); + + // Assert + expect((report.config).groupId).toEqual(testGroupId); + }); + + it('should get groupId from embeddUrl is not specified in config', function () { + // Arrange + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?groupId=DIFFERENTID`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + const configuration: embed.IEmbedConfiguration = { id: 'fakeId' }; + + // Act + const report = powerbi.embed($reportContainer[0], configuration); + + // Assert + expect((report.config).groupId).toEqual('DIFFERENTID'); + }); + + it('should get groupId undefined if not specified in embeddUrl or config', function () { + // Arrange + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?reportId=fakeId`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + const configuration: embed.IEmbedConfiguration = { id: 'fakeId' }; + + // Act + const report = powerbi.embed($reportContainer[0], configuration); + + // Assert + expect((report.config).groupId).toBeUndefined(); + }); + + it('should get filterPaneEnabled setting from attribute from config and then attribute', function () { + // Arrange + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.embed($reportContainer[0]); + + // Assert + expect(report.config.settings.filterPaneEnabled).toEqual(false); + }); + + it('should get navContentPaneEnabled setting from attribute from config and then attribute', function () { + // Arrange + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.embed($reportContainer[0]); + + // Assert + expect(report.config.settings.navContentPaneEnabled).toEqual(false); + }); + + it('if component is already embedded in element re-use the existing component by calling load with the new information', function () { + // Arrange + const $element = $('
') + .appendTo('#powerbi-fixture'); + + const component = powerbi.embed($element[0]); + spyOn(component, "load"); + + const testConfiguration: embed.IEmbedConfiguration = { + accessToken: "fakeToken", + embedUrl: 'fakeUrl', + id: 'report2', + }; + + // Act + const component2 = powerbi.embed($element[0], testConfiguration); + + const actualConfig = component2.config; + + // Assert + expect(component.load).toHaveBeenCalled(); + expect(actualConfig.accessToken).toEqual(testConfiguration.accessToken); + expect(actualConfig.embedUrl).toEqual(testConfiguration.embedUrl); + expect(actualConfig.id).toEqual(testConfiguration.id); + + expect(component2).toBe(component); + }); + + it('if report embed component was not previously created, creates an instance and return it', function () { + // Arrange + let component = $('
') + .appendTo('#powerbi-fixture'); + + // Act + let report = powerbi.embed(component[0]); + + // Assert + expect(report).toBeDefined(); + }); + + it('if dashboard embed component was not previously created, creates an instance and return it', function () { + // Arrange + let component = $('
') + .appendTo('#powerbi-fixture'); + + // Act + let dashboard = powerbi.embed(component[0]); + + // Assert + expect(dashboard).toBeDefined(); + }); + + it("looks for a token first from attribute 'powerbi-access-token'", function () { + // Arrange + let embedUrl = 'https://embedded.powerbi.com/appTokenReportEmbed?reportId=ABC123'; + let testToken = "fakeToken1"; + let $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + powerbi.embed($reportContainer[0]); + + // Assert + let report = powerbi.get($reportContainer[0]); + let accessToken = report.config.accessToken; + + expect(accessToken).toEqual(testToken); + }); + + it("if token is not found by attribute 'powerbi-access-token', fallback to using global", function () { + // Arrange + let embedUrl = 'https://embedded.powerbi.com/appTokenReportEmbed?reportId=ABC123'; + let testToken = "fakeToken1"; + let $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + let originalToken = powerbi.accessToken; + powerbi.accessToken = testToken; + + // Act + powerbi.embed($reportContainer[0]); + + // Assert + let report = powerbi.get($reportContainer[0]); + let accessToken = report.config.accessToken; + + expect(accessToken).toEqual(testToken); + + // Cleanup + powerbi.accessToken = originalToken; + }); + + describe('createReport', function () { + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; + const accessToken = 'ABC123'; + + it('if attempting to createReport without specifying an embed url, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + // Act + const attemptCreate = (): void => { + powerbi.createReport(component[0], { embedUrl: null, accessToken: accessToken, datasetId: '123' }); + }; + + // Assert + expect(attemptCreate).toThrowError(Error); + }); + + it('if attempting to createReport without specifying an access token, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + const originalToken = powerbi.accessToken; + powerbi.accessToken = undefined; + + // Act + const attemptCreate = (): void => { + powerbi.createReport(component[0], { embedUrl: embedUrl, accessToken: null, datasetId: '123' }); + }; + + // Assert + expect(attemptCreate).toThrowError(Error); + + // Cleanup + powerbi.accessToken = originalToken; + }); + + it('if attempting to createReport without specifying an datasetId, throw error', function () { + // Arrange + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const attemptCreate = (): void => { + powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken }); + }; + + // Assert + expect(attemptCreate).toThrowError(); + }); + + }); + + describe('findIdFromEmbedUrl of Create', function () { + it('should return value of datasetId query parameter in embedUrl', function () { + // Arrange + const testDatasetId = "ABC123"; + const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed?datasetId=${testDatasetId}`; + + // Act + const datasetId = create.Create.findIdFromEmbedUrl(testEmbedUrl); + + // Assert + expect(datasetId).toEqual(testDatasetId); + }); + + it('should return undefinded if the datasetId parameter is not in the url', function () { + // Arrange + const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed`; + + // Act + const datasetId = create.Create.findIdFromEmbedUrl(testEmbedUrl); + + // Assert + expect(datasetId).toBeUndefined(); + }); + + it('should get datasetId from configuration first', function () { + // Arrange + const testDatasetId = "ABC123"; + const accessToken = 'ABC123'; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?datasetId=DIFFERENTID`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken, datasetId: testDatasetId }); + + // Assert + expect(report.createConfig.datasetId).toEqual(testDatasetId); + }); + + it('should fallback to using datasetId from embedUrl if not supplied in create configuration', function () { + // Arrange + const testDatasetId = "ABC123"; + const accessToken = 'ABC123'; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?datasetId=${testDatasetId}`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken }); + + // Assert + expect(report.createConfig.datasetId).toEqual(testDatasetId); + }); + + it('theme should be in create config if exists is embedConfig', function () { + // Arrange + + const testDatasetId = "ABC123"; + const accessToken = 'ABC123'; + const theme = { themeJson: { name: "Theme ABC 123" } }; + const embedUrl = `https://app.powerbi.com/reportEmbed?datasetId=${testDatasetId}`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken, theme: theme }); + + // Assert + expect(report.createConfig.theme).toEqual(theme); + }); + + it('theme should be undefined in create config if not exists is embedConfig', function () { + // Arrange + + const testDatasetId = "ABC123"; + const accessToken = 'ABC123'; + const embedUrl = `https://app.powerbi.com/reportEmbed?datasetId=${testDatasetId}`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken }); + + // Assert + expect(report.createConfig.theme).toBeUndefined(); + }); + }); + + describe('reports', function () { + it('creates report iframe from embedUrl', function () { + // Arrange + let embedUrl = 'https://embedded.powerbi.com/appTokenReportEmbed?reportId=ABC123'; + let $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + powerbi.embed($reportContainer[0], { uniqueId: "fakeUid" }); + + // Assert + let iframe = $reportContainer.find('iframe'); + expect(iframe.length).toEqual(1); + expect(iframe.attr('src')).toEqual(embedUrl + "&uid=fakeUid"); + }); + + describe('findIdFromEmbedUrl', function () { + it('should return value of reportId query parameter in embedUrl', function () { + // Arrange + const testReportId = "ABC123"; + const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed?reportId=${testReportId}`; + + // Act + const reportId = report.Report.findIdFromEmbedUrl(testEmbedUrl); + + // Assert + expect(reportId).toEqual(testReportId); + }); + + it('should return undefinded if the query parameter is not in the url', function () { + // Arrange + const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed`; + + // Act + const reportId = report.Report.findIdFromEmbedUrl(testEmbedUrl); + + // Assert + expect(reportId).toBeUndefined(); + }); + }); + + it('should get report id from configuration first', function () { + // Arrange + const testReportId = "ABC123"; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?reportId=DIFFERENTID`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + const configuration: embed.IEmbedConfiguration = { id: testReportId }; + + // Act + const report = powerbi.embed($reportContainer[0], configuration); + + // Assert + expect((report.config).id).toEqual(testReportId); + }); + + it('should fallback to using id from attribute if not supplied in embed/load configuration', function () { + // Arrange + const testReportId = "ABC123"; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?reportId=DIFFERENTID`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.embed($reportContainer[0]); + const config: embed.IEmbedConfiguration = report.config; + // Assert + expect(config.id).toEqual(testReportId); + }); + + it('should fallback to using id from embedUrl if not supplied in embed/load configuration or attribute', function () { + // Arrange + const testReportId = "ABC123"; + const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?reportId=${testReportId}`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + // Act + const report = powerbi.embed($reportContainer[0]); + + // Assert + expect((report.config).id).toEqual(testReportId); + }); + + it('theme should be in report config if exists is embedConfig', function () { + // Arrange + const testReportId = "ABC123"; + const embedUrl = `https://app.powerbi.com/reportEmbed?reportId=${testReportId}`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + const theme = { themeJson: { name: "Theme ABC 123" } }; + const configuration: embed.IEmbedConfiguration = { theme: theme }; + + // Act + const report = powerbi.embed($reportContainer[0], configuration); + + // Assert + expect((report.config).theme).toEqual(theme); + }); + + it('theme should be undefined in report config if not exists is embedConfig', function () { + // Arrange + const testReportId = "ABC123"; + const embedUrl = `https://app.powerbi.com/reportEmbed?reportId=${testReportId}`; + const $reportContainer = $(`
`) + .appendTo('#powerbi-fixture'); + + const configuration: embed.IEmbedConfiguration = {}; + + // Act + const report = powerbi.embed($reportContainer[0], configuration); + + // Assert + expect((report.config).theme).toBeUndefined(); + }); + }); + + describe('tiles', function () { + it('creates tile iframe from embedUrl', function () { + // Arrange + let embedUrl = 'https://app.powerbi.com/embed?dashboardId=D1&tileId=T1'; + let $tileContainer = $('
') + .appendTo('#powerbi-fixture'); + + // Act + powerbi.embed($tileContainer[0], { dashboardId: "D1", embedUrl: embedUrl }); + + // Assert + let iframe = $tileContainer.find('iframe'); + expect(iframe.length).toEqual(1); + expect(iframe.attr('src').indexOf(embedUrl)).toEqual(0); + }); + }); + }); + + describe('bootstrap', function () { + it('if attempting to bootstrap without specifying a type, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + // Act + const attemptEmbed = (): void => { + powerbi.bootstrap(component[0], {}); + }; + + // Assert + expect(attemptEmbed).toThrowError(Error); + }); + + it('if attempting to bootstrap with an unknown type, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + // Act + const attemptEmbed = (): void => { + powerbi.bootstrap(component[0], {}); + }; + + // Assert + expect(attemptEmbed).toThrowError(Error); + }); + + it('if attempting to bootstrap on existing element, throw error', function () { + // Arrange + const component = $('
') + .appendTo('#powerbi-fixture'); + + const reportEmbedConfig: embed.IEmbedConfiguration = { + type: "report", + id: "fakeReportId", + accessToken: "fakeAccessToken", + embedUrl: "fakeEmbedUrl", + groupId: "fakeGroupId", + }; + + const reportEmbedConfig2: embed.IEmbedConfiguration = { + type: "report", + id: "fakeReportId2", + accessToken: "fakeAccessToken", + embedUrl: "fakeEmbedUrl", + groupId: "fakeGroupId" + }; + + powerbi.embed(component[0], reportEmbedConfig); + + // Act + const attemptBootstrap = (): void => { + powerbi.bootstrap(component[0], reportEmbedConfig2); + }; + + // Assert + expect(attemptBootstrap).toThrowError(Error); + }); + + it('powerbi.embed should use the same iframe is already embedded with powerbi.bootstrap', function () { + // Arrange + const $element = $('
') + .appendTo('#powerbi-fixture'); + + const testConfiguration = { + accessToken: "fakeAccessToken", + embedUrl: 'fakeUrl', + id: 'report2', + type: 'report', + groupId: "fakeGroupId" + }; + + // Act + const component = powerbi.bootstrap($element[0], { + type: 'report', + embedUrl: 'fakeUrl2', + }); + + const component2 = powerbi.embed($element[0], testConfiguration); + const component3 = powerbi.get($element[0]); + + // Assert + expect(component).toBeDefined(); + expect(component2).toBeDefined(); + expect(component2).toBe(component3); + }); + + it('powerbi.bootstrap url with correct locale parameters', function () { + // Arrange + const $reportContainer = $('
') + .appendTo('#powerbi-fixture'); + + const testConfiguration: embed.IEmbedConfiguration = { + embedUrl: 'fakeUrl?reportId=1', + id: 'report2', + type: 'report', + settings: { + localeSettings: { + language: 'languageName', + formatLocale: 'formatName' + } + }, + uniqueId: "fakeUid", + }; + + powerbi.bootstrap($reportContainer[0], testConfiguration); + let iframe = $reportContainer.find('iframe'); + expect(iframe.attr('src')).toEqual('fakeUrl?reportId=1&language=languageName&formatLocale=formatName&uid=fakeUid'); + }); + + it('Cannot use JS SDK if autoAuth in embed url', function () { + const embedUrl = `https://app.powerbi.com/reportEmbed?reportId=ABC123&autoAuth=true`; + const $element = $(`
`) + .appendTo('#powerbi-fixture'); + const reportEmbedConfig = { + type: "report", + id: "fakeReportId", + groupId: "fakeGroupId", + accessToken: "fakeAccessToken", + embedUrl: embedUrl + }; + + let exceptionThrown = false; + try { + powerbi.embed($element[0], reportEmbedConfig); + } + catch (e) { + exceptionThrown = true; + expect(e.message).toBe(EmbedUrlNotSupported); + } + + expect(exceptionThrown).toBe(true); + $element.empty(); + $element.remove(); + }); + }); + + describe('reset', function () { + it('deletes the powerBiEmbed property on the element', function () { + // Arrange + const $element = $('
'); + + const config: embed.IEmbedConfiguration = { + type: 'report', + embedUrl: 'fakeUrl', + id: 'fakeId', + accessToken: 'fakeToken' + }; + + powerbi.embed($element.get(0), config); + + // Act + expect(($element.get(0)).powerBiEmbed).toBeDefined(); + powerbi.reset($element.get(0)); + + // Assert + expect(($element.get(0)).powerBiEmbed).toBeUndefined(); + }); + + it('clears the innerHTML of the element', function () { + // Arrange + const $element = $('
'); + + const config: embed.IEmbedConfiguration = { + type: 'report', + embedUrl: 'fakeUrl', + id: 'fakeReportId', + accessToken: 'fakeToken' + }; + + powerbi.embed($element.get(0), config); + + // Act + let iframe = $element.find('iframe'); + expect(iframe.length).toEqual(1); + powerbi.reset($element.get(0)); + + // Assert + expect($element.html()).toEqual(''); + }); + + it('removes the powerbi instance from the list of embeds', function () { + // Arrange + const $element = $('
'); + const testEmbedConfig = { + type: 'report', + embedUrl: 'fakeUrl', + id: 'fakeReportId', + accessToken: 'fakeToken', + uniqueId: 'fakeUniqeId' + }; + powerbi.embed($element.get(0), testEmbedConfig); + + // Act + const report = powerbi.find(testEmbedConfig.uniqueId); + expect(report).toBeDefined(); + + powerbi.reset($element.get(0)); + + // Assert + const report2 = powerbi.find(testEmbedConfig.uniqueId); + expect(report2).toBeUndefined(); + }); + }); +}); diff --git a/test/test.spec.ts b/test/test.spec.ts index 30efaa66..2817e801 100644 --- a/test/test.spec.ts +++ b/test/test.spec.ts @@ -1,6166 +1,68 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import * as utils from '../src/util'; import * as service from '../src/service'; -import * as embed from '../src/embed'; -import * as report from '../src/report'; -import * as visual from '../src/visual'; -import * as create from '../src/create'; -import * as dashboard from '../src/dashboard'; -import * as page from '../src/page'; -import * as sdkConfig from '../src/config'; -import * as visualDescriptor from '../src/visualDescriptor'; -import * as Wpmp from 'window-post-message-proxy'; -import * as Hpm from 'http-post-message'; -import * as Router from 'powerbi-router'; -import * as models from 'powerbi-models'; -import { spyApp, setupEmbedMockApp } from './utility/mockEmbed'; import * as factories from '../src/factories'; -import { spyWpmp } from './utility/mockWpmp'; -import { spyHpm } from './utility/mockHpm'; -import { spyRouter } from './utility/mockRouter'; -import * as util from '../src/util'; -import { APINotSupportedForRDLError, EmbedUrlNotSupported } from '../src/errors' // Avoid adding new tests to this file, create another spec file instead. -declare global { - interface Window { - __karma__: any; - } -} - -function ValidateDashboardConfigurationWorksAsExpected(pageView: string, exceptionExpected: boolean, powerbi: service.Service) { - const embedUrl = `https://app.powerbi.com/dashboardEmbed`; - const component = $(`
`) - .appendTo('#powerbi-fixture'); - - const dashboardEmbedConfig = { - type: "dashboard", - id: "fakeReportId", - groupId: "fakeGroupId", - accessToken: "fakeAccessToken", - embedUrl: "fakeEmbedUrl", - pageView: pageView - }; - - var exceptionThrown = false; - // Act - try { - powerbi.embed(component[0], dashboardEmbedConfig); - } - catch (e) { - exceptionThrown = true; - } - - // Assert - expect(exceptionThrown).toBe(exceptionExpected); -} - -let logMessages = (window.__karma__.config.args[0] === 'logMessages'); - -describe('service', function () { +describe('embed', function () { let powerbi: service.Service; - let $element: JQuery; + let container: HTMLDivElement; + let iframe: HTMLIFrameElement; - beforeAll(function () { + beforeEach(function () { powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory); powerbi.accessToken = 'ABC123'; - $element = $('
').appendTo(document.body); - }); + container = document.createElement('iframe'); + container.setAttribute("powerbi-embed-url", "https://app.powerbi.com/reportEmbed?reportId=ABC123"); + container.setAttribute("powerbi-type", "report"); + document.body.appendChild(container); - afterAll(function () { - $element.remove(); - powerbi.wpmp.stop(); + powerbi.embed(container); + iframe = container.getElementsByTagName('iframe')[0]; }); afterEach(function () { - $element.empty(); - }); - - it('is defined', function () { - expect(powerbi).toBeDefined(); + powerbi.reset(container); + container.remove(); + powerbi.wpmp.stop(); }); - describe('init', function () { - it('embeds all components found in the DOM', function () { - // Arrange - const elements = [ - '
', - '
', - ]; - - elements.forEach(element => { - $(element).appendTo('#powerbi-fixture'); - }); - - // Act - powerbi.init(); - - // Assert - // If embed element has iframe inside it, assume embed action occurred - const iframes = document.querySelectorAll('[powerbi-embed-url] iframe'); - expect(iframes.length).toEqual(2); - }); - - it('embeds all components found in the DOM without id attribute', function () { - // Arrange - const elements = [ - '
', - '
', - ]; - - elements.forEach(element => { - $(element).appendTo('#powerbi-fixture'); - }); - - // Act - powerbi.init(); - - // Assert - // If embed element has iframe inside it, assume embed action occurred - const iframes = document.querySelectorAll('[powerbi-embed-url] iframe'); - expect(iframes.length).toEqual(2); + describe('iframe', function () { + it('has a src', function () { + expect(iframe.src.length).toBeGreaterThan(0); }); - it('embeds all components found in the DOM with duplicate id attribute', function () { - // Arrange - const elements = [ - '
', - '
', - ]; - - elements.forEach(element => { - $(element).appendTo('#powerbi-fixture'); - }); - - // Act - powerbi.init(); - - // Assert - // If embed element has iframe inside it, assume embed action occurred - const iframes = document.querySelectorAll('[powerbi-embed-url] iframe'); - expect(iframes.length).toEqual(2); + it('disables scrollbars by default', function () { + expect(iframe.getAttribute('scrolling')).toEqual('no'); }); - }); - - describe('get', function () { - it('if attempting to get a powerbi component on an element which was not embedded, throw an error', function () { - // Arrange - const $component = $('
'); - // Act - const attemptGet = () => { - powerbi.get($component[0]); - }; - - // Assert - expect(attemptGet).toThrowError(Error); + it('sets width/height to 100%', function () { + expect(iframe.style.width).toEqual('100%'); + expect(iframe.style.height).toEqual('100%'); }); - - it('calling get on element with embeded report component returns the instance', function () { - // Arrange - const $element = $('
') - .appendTo('#powerbi-fixture'); - - const componentInstance = powerbi.embed($element[0]); - - // Act - const componentInstance2 = powerbi.get($element[0]); - - // Assert - expect(componentInstance).toEqual(componentInstance2); - }) - - it('calling get on element with embeded dashboard component returns the instance', function () { - // Arrange - const $element = $('
') - .appendTo('#powerbi-fixture'); - - const componentInstance = powerbi.embed($element[0]); - - // Act - const componentInstance2 = powerbi.get($element[0]); - - // Assert - expect(componentInstance).toEqual(componentInstance2); - }) }); - describe('embed', function () { - it('if attempting to embed without specifying a type, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - // Act - const attemptEmbed = () => { - powerbi.embed(component[0]); - }; - - // Assert - expect(attemptEmbed).toThrowError(Error); - }); - - it('if attempting to embed with an unknown type, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - // Act - const attemptEmbed = () => { - powerbi.embed(component[0]); - }; - - // Assert - expect(attemptEmbed).toThrowError(Error); - }); - - it('if attempting to embed on existing element with different type than previous embed, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - const reportEmbedConfig: embed.IEmbedConfiguration = { - type: "report", - id: "fakeReportId", - accessToken: "fakeAccessToken", - embedUrl: "fakeEmbedUrl", - groupId: "fakeGroupId", - }; - - const dashboardEmbedConfig: embed.IEmbedConfiguration = { - type: "dashboard", - id: "fakeDashboardId", - accessToken: "fakeAccessToken", - embedUrl: "fakeEmbedUrl", - groupId: "fakeGroupId" - }; - - powerbi.embed(component[0], reportEmbedConfig); - - // Act - const attemptEmbed = () => { - powerbi.embed(component[0], dashboardEmbedConfig); - }; - - // Assert - expect(attemptEmbed).toThrowError(Error); - }); - - it('if Create is already embedded in element re-use the existing component by calling load with the new information', function () { - // Arrange - const $element = $('
') - .appendTo('#powerbi-fixture'); - - const testConfiguration = { - accessToken: "fakeAccessToken", - embedUrl: 'fakeUrl', - id: 'report2', - type: 'report', - groupId: "fakeGroupId" - }; - - const createConfig: embed.IEmbedConfiguration = { - datasetId: "fakeDashboardId", - accessToken: "fakeAccessToken", - embedUrl: "fakeEmbedUrl", - groupId: "fakeGroupId" - }; - - // Act - const component = powerbi.createReport($element[0], createConfig); - const component2 = powerbi.embed($element[0], testConfiguration); - const component3 = powerbi.get($element[0]); - - // Assert - //expect(component.createReport).toHaveBeenCalledWith(createConfig); - expect(component).toBeDefined(); - expect(component2).toBeDefined(); - expect(component2).toBe(component3); - }); - - it('Create embed url with correct locale parameters', function () { - // Arrange - const $reportContainer = $('
') - .appendTo('#powerbi-fixture'); - - const testConfiguration: embed.IEmbedConfiguration = { - accessToken: "fakeAccessToken", - embedUrl: 'fakeUrl?reportId=1', - id: 'report2', - type: 'report', - settings: { - localeSettings: { - language: 'languageName', - formatLocale: 'formatName' - } - }, - groupId: "fakeGroupId", - uniqueId: "fakeUid", - }; - - powerbi.embed($reportContainer[0], testConfiguration); - var iframe = $reportContainer.find('iframe'); - expect(iframe.attr('src')).toEqual('fakeUrl?reportId=1&language=languageName&formatLocale=formatName&uid=fakeUid'); - }); - - it('if attempting to embed without specifying an embed url, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - // Act - const attemptEmbed = () => { - const configuration: embed.IEmbedConfiguration = { type: "report", embedUrl: null, accessToken: null, id: null }; - powerbi.embed(component[0], configuration); - }; - - // Assert - expect(attemptEmbed).toThrowError(Error); - }); - - it('if attempting to embed without specifying an access token, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - const originalToken = powerbi.accessToken; - powerbi.accessToken = undefined; - - // Act - const attemptEmbed = () => { - const configuration: embed.IEmbedConfiguration = { type: "report", embedUrl: null, accessToken: null, id: null }; - powerbi.embed(component[0], configuration); - }; - - // Assert - expect(attemptEmbed).toThrowError(Error); - - // Cleanup - powerbi.accessToken = originalToken; - }); - - it('if attempting to embed without specifying an id, throw error', function () { - // Arrange - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const attemptToEmbed = () => { - powerbi.embed($reportContainer[0]); - }; - - // Assert - expect(attemptToEmbed).toThrowError(); - }); - - it('if attempting to embed a dashboard with an invalid pageView, throw error', function () { - ValidateDashboardConfigurationWorksAsExpected("notValid", true, powerbi); - }); - - it('if attempting to embed a dashboard with a pageView equals fitToWidth, don\'t throw error', function () { - ValidateDashboardConfigurationWorksAsExpected("fitToWidth", false, powerbi); - }); - - it('if attempting to embed a dashboard with a pageView equals oneColumn, don\'t throw error', function () { - ValidateDashboardConfigurationWorksAsExpected("oneColumn", false, powerbi); - }); - - it('if attempting to embed a dashboard with a pageView equals actualSize, don\'t throw error', function () { - ValidateDashboardConfigurationWorksAsExpected("actualSize", false, powerbi); - }); - - it('if attempting to embed a dashboard with an undefined pageView, don\'t throw error', function () { - ValidateDashboardConfigurationWorksAsExpected(undefined, false, powerbi); - }); - - it('should get uqiqueId from config first', function () { - // Arrange - const testUniqueId = 'fakeUniqueId'; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.embed($reportContainer[0], { uniqueId: testUniqueId }); - - // Assert - expect(report.config.uniqueId).toEqual(testUniqueId); - }); - - it('should get uqiqueId from name attribute if uniqueId is not specified in config', function () { - // Arrange - const testUniqueId = 'fakeUniqueId'; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.embed($reportContainer[0]); - - // Assert - expect(report.config.uniqueId).toEqual(testUniqueId); - }); - - it('should generate uqiqueId if uniqueId is not specified in config or attribute', function () { - // Arrange - const testUniqueId = 'fakeUniqueId'; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.embed($reportContainer[0]); - - // Assert - expect(report.config.uniqueId).toEqual(jasmine.any(String)); - }); - - it('should get group id from configuration first', function () { - // Arrange - const testGroupId = "ABC123"; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?groupId=DIFFERENTID`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - const configuration: embed.IEmbedConfiguration = { id: 'fakeId', groupId: testGroupId }; - - // Act - const report = powerbi.embed($reportContainer[0], configuration); - - // Assert - expect((report.config).groupId).toEqual(testGroupId); - }); - - it('should get groupId from embeddUrl is not specified in config', function () { - // Arrange - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?groupId=DIFFERENTID`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - const configuration: embed.IEmbedConfiguration = { id: 'fakeId' }; - - // Act - const report = powerbi.embed($reportContainer[0], configuration); - - // Assert - expect((report.config).groupId).toEqual('DIFFERENTID'); - }); - - it('should get groupId undefined if not specified in embeddUrl or config', function () { - // Arrange - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?reportId=fakeId`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - const configuration: embed.IEmbedConfiguration = { id: 'fakeId' }; - - // Act - const report = powerbi.embed($reportContainer[0], configuration); - - // Assert - expect((report.config).groupId).toBeUndefined(); - }); - - it('should get filterPaneEnabled setting from attribute from config and then attribute', function () { - // Arrange - const testUniqueId = 'fakeUniqueId'; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.embed($reportContainer[0]); - - // Assert - expect((report.config.settings).filterPaneEnabled).toEqual(false); - }); - - it('should get navContentPaneEnabled setting from attribute from config and then attribute', function () { - // Arrange - const testUniqueId = 'fakeUniqueId'; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.embed($reportContainer[0]); - - // Assert - expect((report.config.settings).navContentPaneEnabled).toEqual(false); - }); - - it('if component is already embedded in element re-use the existing component by calling load with the new information', function () { - // Arrange - const $element = $('
') - .appendTo('#powerbi-fixture'); - - const component = powerbi.embed($element[0]); - spyOn(component, "load"); - - const testConfiguration: embed.IEmbedConfiguration = { - accessToken: "fakeToken", - embedUrl: 'fakeUrl', - id: 'report2', - }; - - // Act - const component2 = powerbi.embed($element[0], testConfiguration); - - const actualConfig = component2.config; - - // Assert - expect(component.load).toHaveBeenCalled(); - expect(actualConfig.accessToken).toEqual(testConfiguration.accessToken); - expect(actualConfig.embedUrl).toEqual(testConfiguration.embedUrl); - expect(actualConfig.id).toEqual(testConfiguration.id); - - expect(component2).toBe(component); - }); - - it('if report embed component was not previously created, creates an instance and return it', function () { - // Arrange - var component = $('
') - .appendTo('#powerbi-fixture'); - - // Act - var report = powerbi.embed(component[0]); - - // Assert - expect(report).toBeDefined(); - }); - - it('if dashboard embed component was not previously created, creates an instance and return it', function () { - // Arrange - var component = $('
') - .appendTo('#powerbi-fixture'); - - // Act - var dashboard = powerbi.embed(component[0]); - - // Assert - expect(dashboard).toBeDefined(); - }); - - it("looks for a token first from attribute 'powerbi-access-token'", function () { - // Arrange - var embedUrl = 'https://embedded.powerbi.com/appTokenReportEmbed?reportId=ABC123'; - var testToken = "fakeToken1"; - var $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - powerbi.embed($reportContainer[0]); - - // Assert - var report = powerbi.get($reportContainer[0]); - var accessToken = report.config.accessToken; - - expect(accessToken).toEqual(testToken); - }); - - it("if token is not found by attribute 'powerbi-access-token', fallback to using global", function () { - // Arrange - var embedUrl = 'https://embedded.powerbi.com/appTokenReportEmbed?reportId=ABC123'; - var testToken = "fakeToken1"; - var $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - var originalToken = powerbi.accessToken; - powerbi.accessToken = testToken; - - // Act - powerbi.embed($reportContainer[0]); - - // Assert - var report = powerbi.get($reportContainer[0]); - var accessToken = report.config.accessToken; - - expect(accessToken).toEqual(testToken); - - // Cleanup - powerbi.accessToken = originalToken; - }); - - describe('createReport', function () { - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed`; - const accessToken = 'ABC123'; - - it('if attempting to createReport without specifying an embed url, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - // Act - const attemptCreate = () => { - powerbi.createReport(component[0], { embedUrl: null, accessToken: accessToken, datasetId: '123' }); - }; - - // Assert - expect(attemptCreate).toThrowError(Error); - }); - - it('if attempting to createReport without specifying an access token, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - const originalToken = powerbi.accessToken; - powerbi.accessToken = undefined; - - // Act - const attemptCreate = () => { - powerbi.createReport(component[0], { embedUrl: embedUrl, accessToken: null, datasetId: '123' }); - }; - - // Assert - expect(attemptCreate).toThrowError(Error); - - // Cleanup - powerbi.accessToken = originalToken; - }); - - it('if attempting to createReport without specifying an datasetId, throw error', function () { - // Arrange - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const attemptCreate = () => { - powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken }); - }; - - // Assert - expect(attemptCreate).toThrowError(); - }); - - }); - - describe('findIdFromEmbedUrl of Create', function () { - it('should return value of datasetId query parameter in embedUrl', function () { - // Arrange - const testDatasetId = "ABC123"; - const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed?datasetId=${testDatasetId}`; - - // Act - const datasetId = create.Create.findIdFromEmbedUrl(testEmbedUrl); - - // Assert - expect(datasetId).toEqual(testDatasetId); - }); - - it('should return undefinded if the datasetId parameter is not in the url', function () { - // Arrange - const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed`; - - // Act - const datasetId = create.Create.findIdFromEmbedUrl(testEmbedUrl); - - // Assert - expect(datasetId).toBeUndefined(); - }); - - it('should get datasetId from configuration first', function () { - // Arrange - const testDatasetId = "ABC123"; - const accessToken = 'ABC123'; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?datasetId=DIFFERENTID`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken, datasetId: testDatasetId }); - - // Assert - expect(report.createConfig.datasetId).toEqual(testDatasetId); - }); - - it('should fallback to using datasetId from embedUrl if not supplied in create configuration', function () { - // Arrange - const testDatasetId = "ABC123"; - const accessToken = 'ABC123'; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?datasetId=${testDatasetId}`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken }); - - // Assert - expect(report.createConfig.datasetId).toEqual(testDatasetId); - }); - - it('theme should be in create config if exists is embedConfig', function () { - // Arrange - - const testDatasetId = "ABC123"; - const accessToken = 'ABC123'; - const theme = { themeJson: { name: "Theme ABC 123" } }; - const embedUrl = `https://app.powerbi.com/reportEmbed?datasetId=${testDatasetId}`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken, theme: theme }); - - // Assert - expect(report.createConfig.theme).toEqual(theme); - }); - - it('theme should be undefined in create config if not exists is embedConfig', function () { - // Arrange - - const testDatasetId = "ABC123"; - const accessToken = 'ABC123'; - const theme = { themeJson: { name: "Theme ABC 123" } }; - const embedUrl = `https://app.powerbi.com/reportEmbed?datasetId=${testDatasetId}`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.createReport($reportContainer[0], { embedUrl: embedUrl, accessToken: accessToken }); - - // Assert - expect(report.createConfig.theme).toBeUndefined(); - }); - }); - - describe('reports', function () { - it('creates report iframe from embedUrl', function () { - // Arrange - var embedUrl = 'https://embedded.powerbi.com/appTokenReportEmbed?reportId=ABC123'; - var $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - let report = powerbi.embed($reportContainer[0], { uniqueId: "fakeUid" }); - - // Assert - var iframe = $reportContainer.find('iframe'); - expect(iframe.length).toEqual(1); - expect(iframe.attr('src')).toEqual(embedUrl + "&uid=fakeUid"); - }); - - describe('findIdFromEmbedUrl', function () { - it('should return value of reportId query parameter in embedUrl', function () { - // Arrange - const testReportId = "ABC123"; - const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed?reportId=${testReportId}`; - - // Act - const reportId = report.Report.findIdFromEmbedUrl(testEmbedUrl); - - // Assert - expect(reportId).toEqual(testReportId); - }); - - it('should return undefinded if the query parameter is not in the url', function () { - // Arrange - const testEmbedUrl = `http://embedded.powerbi.com/appTokenReportEmbed`; - - // Act - const reportId = report.Report.findIdFromEmbedUrl(testEmbedUrl); - - // Assert - expect(reportId).toBeUndefined(); - }); - }); - - it('should get report id from configuration first', function () { - // Arrange - const testReportId = "ABC123"; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?reportId=DIFFERENTID`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - const configuration: embed.IEmbedConfiguration = { id: testReportId }; - - // Act - const report = powerbi.embed($reportContainer[0], configuration); - - // Assert - expect((report.config).id).toEqual(testReportId); - }); - - it('should fallback to using id from attribute if not supplied in embed/load configuration', function () { - // Arrange - const testReportId = "ABC123"; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?reportId=DIFFERENTID`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.embed($reportContainer[0]); - const config: embed.IEmbedConfiguration = report.config; - // Assert - expect(config.id).toEqual(testReportId); - }); - - it('should fallback to using id from embedUrl if not supplied in embed/load configuration or attribute', function () { - // Arrange - const testReportId = "ABC123"; - const embedUrl = `https://embedded.powerbi.com/appTokenReportEmbed?reportId=${testReportId}`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - // Act - const report = powerbi.embed($reportContainer[0]); - - // Assert - expect((report.config).id).toEqual(testReportId); - }); - - it('theme should be in report config if exists is embedConfig', function () { - // Arrange - const testReportId = "ABC123"; - const embedUrl = `https://app.powerbi.com/reportEmbed?reportId=${testReportId}`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - const theme = { themeJson: { name: "Theme ABC 123" } }; - const configuration: embed.IEmbedConfiguration = { theme: theme }; - - // Act - const report = powerbi.embed($reportContainer[0], configuration); - - // Assert - expect((report.config).theme).toEqual(theme); - }); - - it('theme should be undefined in report config if not exists is embedConfig', function () { - // Arrange - const testReportId = "ABC123"; - const embedUrl = `https://app.powerbi.com/reportEmbed?reportId=${testReportId}`; - const $reportContainer = $(`
`) - .appendTo('#powerbi-fixture'); - - const theme = { themeJson: { name: "Theme ABC 123" } }; - const configuration: embed.IEmbedConfiguration = {}; - - // Act - const report = powerbi.embed($reportContainer[0], configuration); - - // Assert - expect((report.config).theme).toBeUndefined(); - }); - }); - - xdescribe('tiles', function () { - it('creates tile iframe from embedUrl', function () { - // Arrange - var embedUrl = 'https://app.powerbi.com/embed?dashboardId=D1&tileId=T1'; - var $tileContainer = $('
') - .appendTo('#powerbi-fixture'); - - // Act - let tile = powerbi.embed($tileContainer[0]); + describe('fullscreen', function () { + it('sets the iframe as the fullscreen element', function () { + let requestFullscreenSpy = jasmine.createSpy(); + iframe.requestFullscreen = requestFullscreenSpy; + let report = powerbi.get(container); + report.fullscreen(); - // Assert - var iframe = $tileContainer.find('iframe'); - expect(iframe.length).toEqual(1); - expect(iframe.attr('src')).toEqual(embedUrl); - }); + expect(requestFullscreenSpy).toHaveBeenCalled(); }); }); - describe('bootstrap', function () { - it('if attempting to bootstrap without specifying a type, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - // Act - const attemptEmbed = () => { - powerbi.bootstrap(component[0], {}); - }; - - // Assert - expect(attemptEmbed).toThrowError(Error); - }); - - it('if attempting to bootstrap with an unknown type, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - // Act - const attemptEmbed = () => { - powerbi.bootstrap(component[0], {}); - }; - - // Assert - expect(attemptEmbed).toThrowError(Error); - }); - - it('if attempting to bootstrap on existing element, throw error', function () { - // Arrange - const component = $('
') - .appendTo('#powerbi-fixture'); - - const reportEmbedConfig: embed.IEmbedConfiguration = { - type: "report", - id: "fakeReportId", - accessToken: "fakeAccessToken", - embedUrl: "fakeEmbedUrl", - groupId: "fakeGroupId", - }; - - const reportEmbedConfig2: embed.IEmbedConfiguration = { - type: "report", - id: "fakeReportId2", - accessToken: "fakeAccessToken", - embedUrl: "fakeEmbedUrl", - groupId: "fakeGroupId" - }; - - powerbi.embed(component[0], reportEmbedConfig); - - // Act - const attemptBootstrap = () => { - powerbi.bootstrap(component[0], reportEmbedConfig2); - }; - - // Assert - expect(attemptBootstrap).toThrowError(Error); - }); - - it('powerbi.embed should use the same iframe is already embedded with powerbi.bootstrap', function () { - // Arrange - const $element = $('
') - .appendTo('#powerbi-fixture'); - - const testConfiguration = { - accessToken: "fakeAccessToken", - embedUrl: 'fakeUrl', - id: 'report2', - type: 'report', - groupId: "fakeGroupId" - }; - - // Act - const component = powerbi.bootstrap($element[0], { - type: 'report', - embedUrl: 'fakeUrl2', - }); - - const component2 = powerbi.embed($element[0], testConfiguration); - const component3 = powerbi.get($element[0]); - - // Assert - expect(component).toBeDefined(); - expect(component2).toBeDefined(); - expect(component2).toBe(component3); - }); - - it('powerbi.bootstrap url with correct locale parameters', function () { - // Arrange - const $reportContainer = $('
') - .appendTo('#powerbi-fixture'); - - const testConfiguration: embed.IEmbedConfiguration = { - embedUrl: 'fakeUrl?reportId=1', - id: 'report2', - type: 'report', - settings: { - localeSettings: { - language: 'languageName', - formatLocale: 'formatName' - } - }, - uniqueId: "fakeUid", - }; - - powerbi.bootstrap($reportContainer[0], testConfiguration); - var iframe = $reportContainer.find('iframe'); - expect(iframe.attr('src')).toEqual('fakeUrl?reportId=1&language=languageName&formatLocale=formatName&uid=fakeUid'); - }); - - it('Cannot use JS SDK if autoAuth in embed url', function () { - const embedUrl = `https://app.powerbi.com/reportEmbed?reportId=ABC123&autoAuth=true`; - const $element = $(`
`) - .appendTo('#powerbi-fixture'); - const reportEmbedConfig = { - type: "report", - id: "fakeReportId", - groupId: "fakeGroupId", - accessToken: "fakeAccessToken", - embedUrl: embedUrl - }; - - var exceptionThrown = false; - try { - powerbi.embed($element[0], reportEmbedConfig); - } - catch (e) { - exceptionThrown = true; - expect(e.message).toBe(EmbedUrlNotSupported) - } - - expect(exceptionThrown).toBe(true); - $element.empty(); - $element.remove(); - }); - }); - - describe('reset', function () { - it('deletes the powerBiEmbed property on the element', function () { - // Arrange - const $element = $('
'); - - const config: embed.IEmbedConfiguration = { - type: 'report', - embedUrl: 'fakeUrl', - id: 'fakeId', - accessToken: 'fakeToken' - }; - - powerbi.embed($element.get(0), config); - - // Act - expect(($element.get(0)).powerBiEmbed).toBeDefined(); - powerbi.reset($element.get(0)); - - // Assert - expect(($element.get(0)).powerBiEmbed).toBeUndefined(); - }); - - it('clears the innerHTML of the element', function () { - // Arrange - const $element = $('
'); - - const config: embed.IEmbedConfiguration = { - type: 'report', - embedUrl: 'fakeUrl', - id: 'fakeReportId', - accessToken: 'fakeToken' - }; - - powerbi.embed($element.get(0), config); - - // Act - var iframe = $element.find('iframe'); - expect(iframe.length).toEqual(1); - powerbi.reset($element.get(0)); - - // Assert - expect($element.html()).toEqual(''); - }); - - it('removes the powerbi instance from the list of embeds', function () { - // Arrange - const $element = $('
'); - const testEmbedConfig = { - type: 'report', - embedUrl: 'fakeUrl', - id: 'fakeReportId', - accessToken: 'fakeToken', - uniqueId: 'fakeUniqeId' - }; - powerbi.embed($element.get(0), testEmbedConfig); - - // Act - const report = powerbi.find(testEmbedConfig.uniqueId); - expect(report).toBeDefined(); - - powerbi.reset($element.get(0)); - - // Assert - const report2 = powerbi.find(testEmbedConfig.uniqueId); - expect(report2).toBeUndefined(); - }); - }); -}); - -describe('embed', function () { - let powerbi: service.Service; - let $element: JQuery; - let $container: JQuery; - let $iframe: JQuery; - - beforeAll(function () { - powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory); - powerbi.accessToken = 'ABC123'; - $element = $('
').appendTo(document.body); - }); - - beforeEach(function () { - $container = $('
') - .appendTo('#powerbi-fixture'); - - powerbi.embed($container[0]); - $iframe = $container.find('iframe'); - }); - - afterEach(function () { - $element.empty(); - }); - - afterAll(function () { - $element.remove(); - powerbi.wpmp.stop(); - }); - - describe('iframe', function () { - it('has a src', function () { - expect($iframe.attr('src').length).toBeGreaterThan(0); - }); - - it('disables scrollbars by default', function () { - expect($iframe.attr('scrolling')).toEqual('no'); - }); - - it('sets width/height to 100%', function () { - expect($iframe[0].style.width).toEqual('100%'); - expect($iframe[0].style.height).toEqual('100%'); - }); - }); - - describe('fullscreen', function () { - it('sets the iframe as the fullscreen element', function () { - var report = powerbi.get($container[0]); - report.fullscreen(); - - expect(document["webkitFullscreenElement"] === $iframe[0]); - }); - }); - - describe('exitFullscreen', function () { - it('clears the iframe fullscreen element', function () { - var report = powerbi.get($container[0]); - report.fullscreen(); - report.exitFullscreen(); - - expect(document["webkitFullscreenElement"] !== $iframe[0]); - }); - }); -}); - -describe('Protocol', function () { - let hpm: Hpm.HttpPostMessage; - let wpmp: Wpmp.WindowPostMessageProxy; - let iframe: HTMLIFrameElement; - let iframeHpm: Hpm.HttpPostMessage; - let iframeLoaded: Promise; - - let handler: Wpmp.IMessageHandler; - let spyHandler: { - test: jasmine.Spy, - handle: jasmine.Spy - }; - - beforeAll(function () { - const iframeSrc = "base/test/utility/noop.html"; - const $iframe = $(``).appendTo(document.body); - iframe = $iframe.get(0); - - // Register Iframe side - iframeHpm = setupEmbedMockApp(iframe.contentWindow, window, logMessages, 'ProtocolMockAppWpmp'); - - // Register SDK side WPMP - wpmp = factories.wpmpFactory('HostProxyDefaultNoHandlers', logMessages, iframe.contentWindow); - hpm = factories.hpmFactory(wpmp, iframe.contentWindow, 'testVersion'); - const router = factories.routerFactory(wpmp); - - router.post('/reports/:uniqueId/events/:eventName', (req, res) => { - handler.handle(req); - res.send(202); - }); - - router.post('/reports/:uniqueId/pages/:pageName/events/:eventName', (req, res) => { - handler.handle(req); - res.send(202); - }); - - router.post('/reports/:uniqueId/pages/:pageName/visuals/:visualName/events/:eventName', (req, res) => { - handler.handle(req); - res.send(202); - }); - - handler = { - test: jasmine.createSpy("testSpy").and.returnValue(true), - handle: jasmine.createSpy("handleSpy").and.callFake(function (message: any) { - message.handled = true; - return message; - }) - }; - - spyHandler = handler; - // wpmp.addHandler(handler); - - iframeLoaded = new Promise((resolve, reject) => { - iframe.addEventListener('load', () => { - resolve(null); - }); - }); - }); - - afterAll(function () { - wpmp.stop(); - }); - - beforeEach(() => { - // empty - }); - - afterEach(function () { - spyHandler.test.calls.reset(); - spyHandler.handle.calls.reset(); - }); - - describe('HPM-to-MockApp', function () { - describe('notfound', function () { - it('GET request to uknown url returns 404 Not Found', function (done) { - iframeLoaded - .then(() => { - hpm.get('route/that/does/not/exist') - .catch(response => { - expect(response.statusCode).toEqual(404); - done(); - }); - }); - }); - - it('POST request to uknown url returns 404 Not Found', function (done) { - iframeLoaded - .then(() => { - hpm.post('route/that/does/not/exist', null) - .catch(response => { - expect(response.statusCode).toEqual(404); - done(); - }); - }); - }); - - it('PUT request to uknown url returns 404 Not Found', function (done) { - iframeLoaded - .then(() => { - hpm.put('route/that/does/not/exist', null) - .catch(response => { - expect(response.statusCode).toEqual(404); - done(); - }); - }); - }); - - it('PATCH request to uknown url returns 404 Not Found', function (done) { - iframeLoaded - .then(() => { - hpm.patch('route/that/does/not/exist', null) - .catch(response => { - expect(response.statusCode).toEqual(404); - done(); - }); - }); - }); - - it('DELETE request to uknown url returns 404 Not Found', function (done) { - iframeLoaded - .then(() => { - hpm.delete('route/that/does/not/exist') - .catch(response => { - expect(response.statusCode).toEqual(404); - done(); - }); - }); - }); - }); - - describe('create', function () { - describe('report', function () { - it('POST /report/create returns 400 if the request is invalid', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - create: { - datasetId: "fakeId", - accessToken: "fakeToken", - } - }; - - iframeLoaded - .then(() => { - spyApp.validateCreateReport.and.returnValue(Promise.reject(null)); - - // Act - hpm.post('/report/create', testData.create, { uid: testData.uniqueId }) - .then(() => { - expect(false).toBe(true); - spyApp.validateReportLoad.calls.reset(); - done(); - }) - .catch(response => { - // Assert - expect(spyApp.validateCreateReport).toHaveBeenCalledWith(testData.create); - expect(response.statusCode).toEqual(400); - - // Cleanup - spyApp.validateCreateReport.calls.reset(); - done(); - }); - }); - }); - - it('POST /report/create returns 202 if the request is valid', function (done) { - // Arrange - const testData = { - create: { - datasetId: "fakeId", - accessToken: "fakeToken", - } - }; - - iframeLoaded - .then(() => { - spyApp.validateCreateReport.and.returnValue(Promise.resolve(null)); - // Act - hpm.post('/report/create', testData.create) - .then(response => { - // Assert - expect(spyApp.validateCreateReport).toHaveBeenCalledWith(testData.create); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.validateCreateReport.calls.reset(); - spyApp.reportLoad.calls.reset(); - done(); - }) - .catch(response => { - expect(false).toBe(true); - spyApp.validateCreateReport.calls.reset(); - done(); - }); - }); - }); - }); - }); - - describe('load & prepare', function () { - describe('report', function () { - for (var action of ['load', 'prepare']) { - it(`POST /report/${action} returns 400 if the request is invalid`, function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - load: { - reportId: "fakeId", - accessToken: "fakeToken", - options: { - } - } - }; - - iframeLoaded - .then(() => { - spyApp.validateReportLoad.and.returnValue(Promise.reject(null)); - - // Act - hpm.post(`/report/${action}`, testData.load, { uid: testData.uniqueId }) - .then(() => { - expect(false).toBe(true); - spyApp.validateReportLoad.calls.reset(); - done(); - }) - .catch(response => { - // Assert - expect(spyApp.validateReportLoad).toHaveBeenCalledWith(testData.load); - expect(spyApp.reportLoad).not.toHaveBeenCalledWith(testData.load); - expect(response.statusCode).toEqual(400); - // Cleanup - spyApp.validateReportLoad.calls.reset(); - done(); - }); - }); - }); - - it(`POST /report/${action} returns 202 if the request is valid`, function (done) { - // Arrange - const testData = { - load: { - reportId: "fakeId", - accessToken: "fakeToken", - options: { - } - } - }; - - iframeLoaded - .then(() => { - spyApp.validateReportLoad.and.returnValue(Promise.resolve(null)); - // Act - hpm.post(`/report/${action}`, testData.load) - .then(response => { - // Assert - expect(spyApp.validateReportLoad).toHaveBeenCalledWith(testData.load); - expect(spyApp.reportLoad).toHaveBeenCalledWith(testData.load); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.validateReportLoad.calls.reset(); - spyApp.reportLoad.calls.reset(); - done(); - }); - }); - }); - - it(`POST /report/${action} causes POST /reports/:uniqueId/events/loaded`, function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - load: { - reportId: "fakeId", - accessToken: "fakeToken", - options: { - navContentPaneEnabled: false - } - }, - }; - const testExpectedEvent = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/loaded`, - body: { - initiator: 'sdk' - } - }; - - iframeLoaded - .then(() => { - spyApp.reportLoad.and.returnValue(Promise.resolve(testData.load)); - - // Act - hpm.post(`/report/${action}`, testData.load, { uid: testData.uniqueId }) - .then(response => { - setTimeout(() => { - // Assert - expect(spyApp.validateReportLoad).toHaveBeenCalledWith(testData.load); - expect(spyApp.reportLoad).toHaveBeenCalledWith(testData.load); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); - // Cleanup - spyApp.validateReportLoad.calls.reset(); - spyApp.reportLoad.calls.reset(); - done(); - }); - }); - }); - }); - - it(`POST /report/${action} causes POST /reports/:uniqueId/events/error`, function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - load: { - reportId: "fakeId", - accessToken: "fakeToken", - options: { - navContentPaneEnabled: false - } - }, - error: { - message: "error message" - } - }; - const testExpectedEvent = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/error`, - body: testData.error - }; - - iframeLoaded - .then(() => { - spyApp.reportLoad.and.returnValue(Promise.reject(testData.error)); - - // Act - hpm.post(`/report/${action}`, testData.load, { uid: testData.uniqueId }) - .then(response => { - setTimeout(() => { - // Assert - expect(spyApp.validateReportLoad).toHaveBeenCalledWith(testData.load); - expect(spyApp.reportLoad).toHaveBeenCalledWith(testData.load); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); - // Cleanup - spyApp.validateReportLoad.calls.reset(); - spyApp.reportLoad.calls.reset(); - done(); - }); - }); - }); - }); - } - }); - - describe('dashboard', function () { - it('POST /dashboard/load returns 202 if the request is valid', function (done) { - - // Arrange - const testData = { - load: { - dashboardId: "fakeId", - accessToken: "fakeToken", - options: { - } - } - }; - - iframeLoaded - .then(() => { - spyApp.validateDashboardLoad.and.returnValue(Promise.resolve(null)); - // Act - hpm.post('/dashboard/load', testData.load) - .then(response => { - // Assert - expect(spyApp.validateDashboardLoad).toHaveBeenCalledWith(testData.load); - expect(spyApp.dashboardLoad).toHaveBeenCalledWith(testData.load); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.validateDashboardLoad.calls.reset(); - spyApp.dashboardLoad.calls.reset(); - done(); - }); - }); - }); - - it('POST /dashboard/load returns 400 if the request is invalid', function (done) { - - // Arrange - const testData = { - uniqueId: 'uniqueId', - load: { - dashboardId: "fakeId", - accessToken: "fakeToken", - options: { - } - } - }; - - iframeLoaded - .then(() => { - spyApp.validateDashboardLoad.and.returnValue(Promise.reject(null)); - - // Act - hpm.post('/dashboard/load', testData.load, { uid: testData.uniqueId }) - .then(() => { - expect(false).toBe(true); - spyApp.validateDashboardLoad.calls.reset(); - done(); - }) - .catch(response => { - // Assert - expect(spyApp.validateDashboardLoad).toHaveBeenCalledWith(testData.load); - expect(spyApp.dashboardLoad).not.toHaveBeenCalledWith(testData.load); - expect(response.statusCode).toEqual(400); - // Cleanup - spyApp.validateDashboardLoad.calls.reset(); - done(); - }); - }); - }); - }); - }); - - describe('render', function () { - it('POST /report/render returns 202 if the request is valid', function (done) { - // Arrange - iframeLoaded - .then(() => { - spyApp.render.and.returnValue(Promise.resolve(null)); - // Act - hpm.post('/report/render', null) - .then(response => { - // Assert - expect(spyApp.render).toHaveBeenCalled(); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.render.calls.reset(); - done(); - }); - }); - }); - }); - - describe('pages', function () { - - it('GET /report/pages returns 200 with body as array of pages', function (done) { - // Arrange - const testData = { - expectedPages: [ - { - name: "a" - }, - { - name: "b" - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.getPages.and.returnValue(Promise.resolve(testData.expectedPages)); - // Act - hpm.get('/report/pages') - .then(response => { - // Assert - expect(spyApp.getPages).toHaveBeenCalled(); - const pages = response.body; - // @ts-ignore as testData is not of type IFilter - expect(pages).toEqual(testData.expectedPages); - // Cleanup - spyApp.getPages.calls.reset(); - done(); - }); - }); - }); - - it('GET /report/pages returns 500 with body as error', function (done) { - // Arrange - const testData = { - expectedError: { - message: "could not query pages" - } - }; - - iframeLoaded - .then(() => { - spyApp.getPages.and.returnValue(Promise.reject(testData.expectedError)); - // Act - hpm.get('/report/pages') - .catch(response => { - // Assert - expect(spyApp.getPages).toHaveBeenCalled(); - const error = response.body; - expect(error).toEqual(testData.expectedError); - // Cleanup - spyApp.getPages.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/pages/active returns 400 if request is invalid', function (done) { - // Arrange - const testData = { - page: { - name: "fakeName" - } - }; - - iframeLoaded - .then(() => { - spyApp.validatePage.and.returnValue(Promise.reject(null)); - // Act - hpm.put('/report/pages/active', testData.page) - .catch(response => { - // Assert - expect(spyApp.validatePage).toHaveBeenCalledWith(testData.page); - expect(spyApp.setPage).not.toHaveBeenCalled(); - expect(response.statusCode).toEqual(400); - // Cleanup - spyApp.validatePage.calls.reset(); - spyApp.setPage.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/pages/active returns 202 if request is valid', function (done) { - // Arrange - const testData = { - page: { - name: "fakeName" - } - }; - - iframeLoaded - .then(() => { - spyApp.validatePage.and.returnValue(Promise.resolve(null)); - - // Act - hpm.put('/report/pages/active', testData.page) - .then(response => { - // Assert - expect(spyApp.validatePage).toHaveBeenCalledWith(testData.page); - expect(spyApp.setPage).toHaveBeenCalledWith(testData.page); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.validatePage.calls.reset(); - spyApp.setPage.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/pages/active causes POST /reports/:uniqueId/events/pageChanged', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - page: { - name: "fakeName" - } - }; - const expectedEvent = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/pageChanged`, - body: jasmine.objectContaining({ - initiator: 'sdk' - }) - }; - - iframeLoaded - .then(() => { - spyApp.validatePage.and.returnValue(Promise.resolve(null)); - - // Act - hpm.put('/report/pages/active', testData.page, { uid: testData.uniqueId }) - .then(response => { - // Assert - expect(spyApp.validatePage).toHaveBeenCalledWith(testData.page); - expect(spyApp.setPage).toHaveBeenCalledWith(testData.page); - expect(response.statusCode).toEqual(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(expectedEvent)); - // Cleanup - spyApp.validateReportLoad.calls.reset(); - spyApp.setPage.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/pages/active causes POST /reports/:uniqueId/events/error', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - page: { - name: "fakeName" - }, - error: { - message: "error" - } - }; - const expectedEvent = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/error`, - body: testData.error - }; - - iframeLoaded - .then(() => { - spyApp.validatePage.and.returnValue(Promise.resolve(null)); - spyApp.setPage.and.returnValue(Promise.reject(testData.error)); - - // Act - hpm.put('/report/pages/active', testData.page, { uid: testData.uniqueId }) - .then(response => { - // Assert - expect(spyApp.validatePage).toHaveBeenCalledWith(testData.page); - expect(spyApp.setPage).toHaveBeenCalledWith(testData.page); - expect(response.statusCode).toEqual(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(expectedEvent)); - // Cleanup - spyApp.validateReportLoad.calls.reset(); - spyApp.setPage.calls.reset(); - done(); - }); - }); - }); - }); - - describe('refresh', function () { - it('POST /report/refresh returns 202 if the request is valid', function (done) { - // Arrange - iframeLoaded - .then(() => { - spyApp.refreshData.and.returnValue(Promise.resolve(null)); - // Act - hpm.post('/report/refresh', null) - .then(response => { - // Assert - expect(spyApp.refreshData).toHaveBeenCalled(); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.refreshData.calls.reset(); - done(); - }); - }); - }); - }); - - describe('print', function () { - it('POST /report/print returns 202 if the request is valid', function (done) { - // Arrange - iframeLoaded - .then(() => { - spyApp.print.and.returnValue(Promise.resolve(null)); - // Act - hpm.post('/report/print', null) - .then(response => { - // Assert - expect(spyApp.print).toHaveBeenCalled(); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.print.calls.reset(); - done(); - }); - }); - }); - }); - - describe('switchMode', function () { - it('POST /report/switchMode returns 202 if the request is valid', function (done) { - // Arrange - iframeLoaded - .then(() => { - spyApp.switchMode.and.returnValue(Promise.resolve(null)); - // Act - hpm.post('/report/switchMode/Edit', null) - .then(response => { - // Assert - expect(spyApp.switchMode).toHaveBeenCalled(); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.switchMode.calls.reset(); - done(); - }); - }); - }); - }); - - describe('save', function () { - it('POST /report/save returns 202 if the request is valid', function (done) { - // Arrange - iframeLoaded - .then(() => { - spyApp.save.and.returnValue(Promise.resolve(null)); - // Act - hpm.post('/report/save', null) - .then(response => { - // Assert - expect(spyApp.save).toHaveBeenCalled(); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.save.calls.reset(); - done(); - }); - }); - }); - }); - - describe('saveAs', function () { - it('POST /report/saveAs returns 202 if the request is valid', function (done) { - // Arrange - let saveAsParameters: models.ISaveAsParameters = { name: "reportName" }; - - iframeLoaded - .then(() => { - spyApp.saveAs.and.returnValue(Promise.resolve(null)); - // Act - hpm.post('/report/saveAs', saveAsParameters) - .then(response => { - // Assert - expect(spyApp.saveAs).toHaveBeenCalled(); - expect(spyApp.saveAs).toHaveBeenCalledWith(saveAsParameters); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.saveAs.calls.reset(); - done(); - }); - }); - }); - }); - - describe('setAccessToken', function () { - it('POST /report/token returns 202 if the request is valid', function (done) { - // Arrange - let accessToken: string = "fakeToken"; - - iframeLoaded - .then(() => { - spyApp.setAccessToken.and.returnValue(Promise.resolve(null)); - // Act - hpm.post('/report/token', accessToken) - .then(response => { - // Assert - expect(spyApp.setAccessToken).toHaveBeenCalled(); - expect(spyApp.setAccessToken).toHaveBeenCalledWith(accessToken); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.saveAs.calls.reset(); - done(); - }); - }); - }); - }); - - describe('filters (report level)', function () { - it('GET /report/filters returns 200 with body as array of filters', function (done) { - // Arrange - const testData = { - filters: [ - { - name: "fakeFilter1" - }, - { - name: "fakeFilter2" - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); - - // Act - hpm.get('/report/filters') - .then(response => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - expect(response.statusCode).toEqual(200); - // @ts-ignore as testData is not of type IFilter - expect(response.body).toEqual(testData.filters); - // Cleanup - spyApp.getFilters.calls.reset(); - done(); - }); - }); - }); - - it('GET /report/filters returns 500 with body as error', function (done) { - // Arrange - const testData = { - error: { - message: "internal error" - } - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.reject(testData.error)); - - // Act - hpm.get('/report/filters') - .catch(response => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - expect(response.statusCode).toEqual(500); - expect(response.body).toEqual(testData.error); - // Cleanup - spyApp.getFilters.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/filters returns 400 if request is invalid', function (done) { - // Arrange - const testData = { - filters: [ - { - name: "fakeFilter" - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.reject(null)); - - // Act - hpm.put('/report/filters', testData.filters) - .catch(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).not.toHaveBeenCalled(); - expect(response.statusCode).toEqual(400); - // Cleanup - spyApp.validateFilter.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/filters returns 202 if request is valid', function (done) { - // Arrange - const testData = { - filters: [ - { - name: "fakeFilter" - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.resolve(null)); - - // Act - hpm.put('/report/filters', testData.filters) - .then(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.validateFilter.calls.reset(); - spyApp.setFilters.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/filters will cause POST /reports/:uniqueId/events/filtersApplied', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - filters: [ - { - name: "fakeFilter" - } - ] - }; - const testExpectedEvent = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/filtersApplied` - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.resolve(null)); - - // Act - hpm.put('/report/filters', testData.filters, { uid: testData.uniqueId }) - .then(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); - expect(response.statusCode).toEqual(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); - // Cleanup - spyApp.validateFilter.calls.reset(); - spyApp.setFilters.calls.reset(); - done(); - }); - }); - }); - }); - - describe('filters (page level)', function () { - it('GET /report/pages/xyz/filters returns 200 with body as array of filters', function (done) { - // Arrange - const testData = { - filters: [ - { - name: "fakeFilter1" - }, - { - name: "fakeFilter2" - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); - - // Act - hpm.get('/report/pages/xyz/filters') - .then(response => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - expect(response.statusCode).toEqual(200); - // @ts-ignore as testData is not of type IFilter - expect(response.body).toEqual(testData.filters); - // Cleanup - spyApp.getFilters.calls.reset(); - done(); - }); - }); - }); - - it('GET /report/pages/xyz/filters returns 500 with body as error', function (done) { - // Arrange - const testData = { - error: { - message: "internal error" - } - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.reject(testData.error)); - - // Act - hpm.get('/report/pages/xyz/filters') - .catch(response => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - expect(response.statusCode).toEqual(500); - expect(response.body).toEqual(testData.error); - // Cleanup - spyApp.getFilters.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/pages/xyz/filters returns 400 if request is invalid', function (done) { - // Arrange - const testData = { - filters: [ - { - name: "fakeFilter" - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.reject(null)); - - // Act - hpm.put('/report/pages/xyz/filters', testData.filters) - .catch(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).not.toHaveBeenCalled(); - expect(response.statusCode).toEqual(400); - // Cleanup - spyApp.validatePage.calls.reset(); - spyApp.validateFilter.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/pages/xyz/filters returns 202 if request is valid', function (done) { - // Arrange - const testData = { - filters: [ - { - name: "fakeFilter" - } - ], - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.resolve(null)); - - // Act - hpm.put('/report/pages/xyz/filters', testData.filters) - .then(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.validatePage.calls.reset(); - spyApp.validateFilter.calls.reset(); - spyApp.setFilters.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/pages/xyz/filters will cause POST /reports/:uniqueId/pages/xyz/events/filtersApplied', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - filters: [ - { - name: "fakeFilter" - } - ] - }; - const testExpectedEvent = { - method: 'POST', - url: `/reports/${testData.uniqueId}/pages/xyz/events/filtersApplied` - }; - - iframeLoaded - .then(() => { - spyHandler.handle.calls.reset(); - spyApp.validateFilter.and.returnValue(Promise.resolve(null)); - - // Act - hpm.put('/report/pages/xyz/filters', testData.filters, { uid: testData.uniqueId }) - .then(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); - expect(response.statusCode).toEqual(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); - // Cleanup - spyApp.validatePage.calls.reset(); - spyApp.validateFilter.calls.reset(); - spyApp.setFilters.calls.reset(); - done(); - }); - }); - }); - }); - - describe('filters (visual level)', function () { - it('GET /report/pages/xyz/visuals/uvw/filters returns 200 with body as array of filters', function (done) { - // Arrange - const testData = { - filters: [ - { - name: "fakeFilter1" - }, - { - name: "fakeFilter2" - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); - - // Act - hpm.get('/report/pages/xyz/visuals/uvw/filters') - .then(response => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - expect(response.statusCode).toEqual(200); - // @ts-ignore as testData is not of type IFilter - expect(response.body).toEqual(testData.filters); - // Cleanup - spyApp.getFilters.calls.reset(); - spyApp.validateVisual.calls.reset(); - done(); - }); - }); - }); - - it('GET /report/pages/xyz/visuals/uvw/filters returns 500 with body as error', function (done) { - // Arrange - const testData = { - error: { - message: "internal error" - } - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.reject(testData.error)); - - // Act - hpm.get('/report/pages/xyz/visuals/uvw/filters') - .catch(response => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - expect(response.statusCode).toEqual(500); - expect(response.body).toEqual(testData.error); - // Cleanup - spyApp.getFilters.calls.reset(); - spyApp.validateVisual.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/pages/xyz/visuals/uvw/filters returns 400 if request is invalid', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - filters: [ - { - name: "fakeFilter" - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.reject(null)); - - // Act - hpm.put('/report/pages/xyz/visuals/uvw/filters', testData.filters, { uid: testData.uniqueId }) - .catch(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).not.toHaveBeenCalled(); - expect(response.statusCode).toEqual(400); - // Cleanup - spyApp.validateVisual.calls.reset(); - spyApp.validateFilter.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/pages/xyz/visuals/uvw/filters returns 202 if request is valid', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - filters: [ - { - name: "fakeFilter" - } - ], - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.resolve(null)); - - // Act - hpm.put('/report/pages/xyz/visuals/uvw/filters', testData.filters, { uid: testData.uniqueId }) - .then(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.validateVisual.calls.reset(); - spyApp.validateFilter.calls.reset(); - spyApp.setFilters.calls.reset(); - done(); - }); - }); - }); - - it('PUT /report/:uniqueId/pages/xyz/visuals/uvw/filters will cause POST /reports/:uniqueId/pages/xyz/visuals/uvw/events/filtersApplied', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - filters: [ - { - name: "fakeFilter" - } - ] - }; - const testExpectedEvent = { - method: 'POST', - url: `/reports/${testData.uniqueId}/pages/xyz/visuals/uvw/events/filtersApplied` - }; - - iframeLoaded - .then(() => { - - // Act - hpm.put('/report/pages/xyz/visuals/uvw/filters', testData.filters, { uid: testData.uniqueId }) - .then(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); - expect(response.statusCode).toEqual(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); - // Cleanup - spyApp.validateVisual.calls.reset(); - spyApp.validateFilter.calls.reset(); - spyApp.setFilters.calls.reset(); - done(); - }); - }); - }); - }); - - describe('settings', function () { - - it('PATCH /report/settings returns 400 if request is invalid', function (done) { - // Arrange - const testData = { - settings: { - filterPaneEnabled: false, - navContentPaneEnabled: false - } - }; - - iframeLoaded - .then(() => { - spyApp.validateSettings.and.returnValue(Promise.reject(null)); - - // Act - hpm.patch('/report/settings', testData.settings) - .catch(response => { - // Assert - expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); - expect(spyApp.updateSettings).not.toHaveBeenCalled(); - expect(response.statusCode).toEqual(400); - // Cleanup - spyApp.validateSettings.calls.reset(); - done(); - }); - }); - }); - - it('PATCH /report/settings returns 202 if request is valid', function (done) { - // Arrange - const testData = { - settings: { - filterPaneEnabled: false, - navContentPaneEnabled: false - } - }; - - iframeLoaded - .then(() => { - spyApp.validateSettings.and.returnValue(Promise.resolve(null)); - - // Act - hpm.patch('/report/settings', testData.settings) - .then(response => { - // Assert - expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); - expect(spyApp.updateSettings).toHaveBeenCalledWith(testData.settings); - expect(response.statusCode).toEqual(202); - // Cleanup - spyApp.validateSettings.calls.reset(); - spyApp.updateSettings.calls.reset(); - done(); - }); - }); - }); - - it('PATCH /report/settings causes POST /reports/:uniqueId/events/settingsUpdated', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - settings: { - filterPaneEnabled: false - } - }; - const testExpectedEvent = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/settingsUpdated`, - body: { - initiator: 'sdk', - settings: { - filterPaneEnabled: false, - navContentPaneEnabled: false - } - } - }; - - iframeLoaded - .then(() => { - spyApp.validateSettings.and.returnValue(Promise.resolve(null)); - spyApp.updateSettings.and.returnValue(Promise.resolve(testExpectedEvent.body.settings)); - - // Act - hpm.patch('/report/settings', testData.settings, { uid: testData.uniqueId }) - .then(response => { - // Assert - setTimeout(() => { - expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); - expect(spyApp.updateSettings).toHaveBeenCalledWith(testData.settings); - expect(response.statusCode).toEqual(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedEvent)); - // Cleanup - spyApp.validateSettings.calls.reset(); - spyApp.updateSettings.calls.reset(); - - done(); - }); - }); - }); - }); - }); - }); - - describe('MockApp-to-HPM', function () { - describe('pages', function () { - it('POST /reports/:uniqueId/events/pageChanged when user changes page', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - event: { - initiator: 'user', - newPage: { - name: "fakePageName" - } - } - }; - const testExpectedRequest = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/pageChanged`, - body: testData.event - }; - - iframeLoaded - .then(() => { - - // Act - iframeHpm.post(testExpectedRequest.url, testData.event) - .then(response => { - // Assert - expect(response.statusCode).toBe(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); - - done(); - }); - - // Cleanup - }); - }); - }); - - describe('filters (report level)', function () { - it('POST /reports/:uniqueId/events/filtersApplied when user changes filter', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - event: { - initiator: 'user', - filters: [ - { - name: "fakeFilter" - } - ] - } - }; - const testExpectedRequest = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/filtersApplied`, - body: testData.event - }; - - iframeLoaded - .then(() => { - - // Act - iframeHpm.post(testExpectedRequest.url, testData.event) - .then(response => { - // Assert - expect(response.statusCode).toBe(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); - - done(); - }); - - // Cleanup - }); - }); - }); - - describe('filters (page level)', function () { - it('POST /reports/:uniqueId/pages/xyz/events/filtersApplied when user changes filter', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - event: { - initiator: 'user', - filters: [ - { - name: "fakeFilter" - } - ] - } - }; - const testExpectedRequest = { - method: 'POST', - url: `/reports/${testData.uniqueId}/pages/xyz/events/filtersApplied`, - body: testData.event - }; - - iframeLoaded - .then(() => { - - // Act - iframeHpm.post(testExpectedRequest.url, testData.event) - .then(response => { - // Assert - expect(response.statusCode).toBe(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); - - done(); - }); - - // Cleanup - }); - }); - }); - - describe('filters (visual level)', function () { - it('POST /reports/:uniqueId/pages/xyz/visuals/uvw/events/filtersApplied when user changes filter', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - event: { - initiator: 'user', - filters: [ - { - name: "fakeFilter" - } - ] - } - }; - const testExpectedRequest = { - method: 'POST', - url: `/reports/${testData.uniqueId}/pages/xyz/visuals/uvw/events/filtersApplied`, - body: testData.event - }; - - iframeLoaded - .then(() => { - spyHandler.handle.calls.reset(); - - // Act - iframeHpm.post(testExpectedRequest.url, testData.event) - .then(response => { - // Assert - expect(response.statusCode).toBe(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); - - done(); - }); - - // Cleanup - }); - }); - }); - - describe('settings', function () { - it('POST /reports/:uniqueId/events/settingsUpdated when user changes settings', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - event: { - initiator: 'user', - settings: { - navContentPaneEnabled: true - } - } - }; - const testExpectedRequest = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/settingsUpdated`, - body: testData.event - }; - - iframeLoaded - .then(() => { - - // Act - iframeHpm.post(testExpectedRequest.url, testData.event) - .then(response => { - // Assert - expect(response.statusCode).toBe(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); - - done(); - }); - - // Cleanup - }); - }); - }); - - describe('data selection', function () { - it('POST /reports/:uniqueId/events/dataSelected when user selects data', function (done) { - // Arrange - const testData = { - uniqueId: 'uniqueId', - reportId: 'fakeReportId', - event: { - initiator: 'user', - selection: { - data: true - } - } - }; - const testExpectedRequest = { - method: 'POST', - url: `/reports/${testData.uniqueId}/events/dataSelected`, - body: testData.event - }; - - iframeLoaded - .then(() => { - - // Act - iframeHpm.post(testExpectedRequest.url, testData.event) - .then(response => { - // Assert - expect(response.statusCode).toBe(202); - expect(spyHandler.handle).toHaveBeenCalledWith(jasmine.objectContaining(testExpectedRequest)); - done(); - }); - - // Cleanup - }); - }); - }); - }); -}); - -describe('SDK-to-HPM', function () { - let $reportElement: JQuery; - let $dashboardElement: JQuery; - let $createElement: JQuery; - let iframe: HTMLIFrameElement; - let dashboardIframe: HTMLIFrameElement; - let createIframe: HTMLIFrameElement; - let visualFrame: HTMLIFrameElement; - let powerbi: service.Service; - let report: report.Report; - let create: create.Create; - let dashboard: dashboard.Dashboard; - let embeddedVisual: visual.Visual; - let page1: page.Page; - let visual1: visualDescriptor.VisualDescriptor; - let uniqueId = 'uniqueId'; - let sdkSessionId = 'sdkSessionId'; - let createUniqueId = 'uniqueId'; - let dashboardUniqueId = 'uniqueId'; - let visualUniqueId = 'uniqueId'; - let embedConfiguration: embed.IEmbedConfiguration; - let dashboardEmbedConfiguration: embed.IEmbedConfiguration; - let embedCreateConfiguration: embed.IEmbedConfiguration; - let visualEmbedConfiguration: embed.IVisualEmbedConfiguration; - - let reportConfigurationBck: embed.IEmbedConfigurationBase; - let createConfigurationBck: embed.IEmbedConfigurationBase; - let dashboardEmbedConfigurationBck: embed.IEmbedConfigurationBase; - let visualEmbedConfigurationBck: embed.IEmbedConfigurationBase; - - const iframeSrc = "base/test/utility/noop.html"; - - beforeAll(function () { - const spyHpmFactory: factories.IHpmFactory = () => { - return spyHpm; - }; - const noop: factories.IWpmpFactory = () => { - return null; - }; - - const spyRouterFactory: factories.IRouterFactory = () => { - return spyRouter; - }; - - spyOn(utils, "getTimeDiffInMilliseconds").and.callFake(() => 700); // Prevent requests from being throttled. - - powerbi = new service.Service(spyHpmFactory, noop, spyRouterFactory, { wpmpName: 'SDK-to-HPM report wpmp' }); - - $reportElement = $(`
`) - .appendTo(document.body); - $createElement = $(`
`) - .appendTo(document.body); - $dashboardElement = $(`
`) - .appendTo(document.body); - let $visualElement = $(`
`) - .appendTo(document.body); - - embedConfiguration = { - type: "report", - id: "fakeReportId", - accessToken: 'fakeToken', - embedUrl: iframeSrc - }; - embedCreateConfiguration = { - datasetId: "fakeReportId", - accessToken: 'fakeToken', - embedUrl: iframeSrc - }; - dashboardEmbedConfiguration = { - type: "dashboard", - id: "fakeDashboardId", - accessToken: 'fakeToken', - embedUrl: iframeSrc - }; - visualEmbedConfiguration = { - id: "visual1", - accessToken: 'fakeToken', - embedUrl: iframeSrc, - type: "visual", - pageName: "ReportSection1", - visualName: "VisualContainer1", - width: 1200, - height: 1000 - }; - report = powerbi.embed($reportElement[0], embedConfiguration); - create = powerbi.createReport($createElement[0], embedCreateConfiguration); - dashboard = powerbi.embed($dashboardElement[0], dashboardEmbedConfiguration); - embeddedVisual = powerbi.embed($visualElement[0], visualEmbedConfiguration); - page1 = new page.Page(report, 'xyz'); - visual1 = new visualDescriptor.VisualDescriptor(page1, 'uvw', 'title', 'type', {}); - uniqueId = report.config.uniqueId; - sdkSessionId = powerbi.getSdkSessionId(); - createUniqueId = create.config.uniqueId; - dashboardUniqueId = dashboard.config.uniqueId; - visualUniqueId = embeddedVisual.config.uniqueId; - iframe = $reportElement.find('iframe')[0]; - createIframe = $createElement.find('iframe')[0]; - dashboardIframe = $dashboardElement.find('iframe')[0]; - visualFrame = $visualElement.find('iframe')[0]; - - // Reset load handler - spyHpm.post.calls.reset(); - }); - - afterAll(function () { - powerbi.reset($reportElement.get(0)); - powerbi.reset($dashboardElement.get(0)); - $reportElement.remove(); - $dashboardElement.remove(); - powerbi.wpmp.stop(); - }); - - beforeEach(function () { - reportConfigurationBck = report.config; - createConfigurationBck = create.config; - dashboardEmbedConfigurationBck = dashboard.config; - visualEmbedConfigurationBck = embeddedVisual.config; - }); - - afterEach(function () { - spyHpm.get.calls.reset(); - spyHpm.post.calls.reset(); - spyHpm.patch.calls.reset(); - spyHpm.put.calls.reset(); - spyHpm.delete.calls.reset(); - - spyRouter.get.calls.reset(); - spyRouter.post.calls.reset(); - spyRouter.patch.calls.reset(); - spyRouter.put.calls.reset(); - spyRouter.delete.calls.reset(); - - report.config = reportConfigurationBck; - create.config = createConfigurationBck; - dashboard.config = dashboardEmbedConfigurationBck; - embeddedVisual.config = visualEmbedConfigurationBck; - }); - - describe('report', function () { - - describe('load', function () { - it('report.load() sends POST /report/load with configuration in body', function () { - // Arrange - const testData = { - loadConfiguration: { - id: 'fakeId', - accessToken: 'fakeToken' - }, - response: { - body: null - } - }; - - spyHpm.post.and.returnValue(Promise.resolve(testData.response)); - - // Act - let expectedConfiguration = utils.assign({}, report.config, testData.loadConfiguration) - report.config = expectedConfiguration; - report.iframeLoaded = true; - report.load(); - - // Assert - const expectedHeaders = { - bootstrapped: undefined, - sdkVersion: sdkConfig.default.version, - uid: uniqueId, - sdkSessionId: sdkSessionId - }; - - expect(spyHpm.post).toHaveBeenCalledWith('/report/load', expectedConfiguration, expectedHeaders, iframe.contentWindow); - }); - - it('report.load() returns promise that rejects with validation error if the load configuration is invalid', function (done) { - // Arrange - const testData = { - loadConfiguration: { - id: 'fakeId', - accessToken: 'fakeToken' - }, - errorResponse: { - body: { - message: "invalid configuration object" - } - } - }; - - spyHpm.post.and.returnValue(Promise.reject(testData.errorResponse)); - - // Act - let expectedConfiguration = utils.assign({}, report.config, testData.loadConfiguration); - report.config = expectedConfiguration; - report.load() - .catch(error => { - const expectedHeaders = { - bootstrapped: undefined, - sdkVersion: sdkConfig.default.version, - uid: uniqueId, - sdkSessionId: sdkSessionId - }; - expect(spyHpm.post).toHaveBeenCalledWith('/report/load', expectedConfiguration, expectedHeaders, iframe.contentWindow); - expect(error).toEqual(testData.errorResponse.body); - // Assert - done(); - }); - }); - - it('report.load() returns promise that resolves with null if the report load successful', function (done) { - // Arrange - const testData = { - loadConfiguration: { - id: 'fakeId', - accessToken: 'fakeToken' - }, - response: { - body: null - } - }; - - spyHpm.post.and.returnValue(Promise.resolve(testData.response)); - - // Act - let expectedConfiguration = utils.assign({}, report.config, testData.loadConfiguration); - report.config = expectedConfiguration; - report.load() - .then(response => { - const expectedHeaders = { - bootstrapped: undefined, - sdkVersion: sdkConfig.default.version, - uid: uniqueId, - sdkSessionId: sdkSessionId - }; - - expect(spyHpm.post).toHaveBeenCalledWith('/report/load', expectedConfiguration, expectedHeaders, iframe.contentWindow); - expect(response).toEqual(null); - // Assert - done(); - }); - }); - - it('report.load() updates the internal configuration if the load request was successful', function (done) { - // Arrange - const testData = { - loadConfiguration: { - id: 'newFakeId', - accessToken: 'newFakeToken' - }, - response: { - body: null - } - }; - - spyHpm.post.and.returnValue(Promise.resolve(testData.response)); - - // Act - let expectedConfiguration = utils.assign({}, report.config, testData.loadConfiguration); - report.config = expectedConfiguration; - report.load() - .then(response => { - expect(report.config).toEqual(jasmine.objectContaining(expectedConfiguration)); - expect(response).toEqual(null); - // Assert - done(); - }); - }); - - it('powerbi.embed with visual name sends POST /report/load with custom layout configuration in body', function (done) { - - let testData = { - loadConfiguration: visualEmbedConfiguration, - response: { - body: null - } - }; - - let expectedConfiguration = { - id: visualEmbedConfiguration.id, - accessToken: visualEmbedConfiguration.accessToken, - embedUrl: visualEmbedConfiguration.embedUrl, - type: visualEmbedConfiguration.type, - pageName: visualEmbedConfiguration.pageName, - visualName: visualEmbedConfiguration.visualName, - width: visualEmbedConfiguration.width, - height: visualEmbedConfiguration.height, - groupId: undefined, - uniqueId: embeddedVisual.config.uniqueId, - settings: { - filterPaneEnabled: false, - navContentPaneEnabled: false, - layoutType: models.LayoutType.Custom, - customLayout: { - displayOption: models.DisplayOption.FitToPage, - pageSize: { - type: models.PageSizeType.Custom, - width: testData.loadConfiguration.width, - height: testData.loadConfiguration.height, - }, - pagesLayout: { - "ReportSection1": { - defaultLayout: { - displayState: { - mode: models.VisualContainerDisplayMode.Hidden - } - }, - visualsLayout: { - "VisualContainer1": { - displayState: { - mode: models.VisualContainerDisplayMode.Visible - }, - x: 1, - y: 1, - z: 1, - width: testData.loadConfiguration.width, - height: testData.loadConfiguration.height - } - } - } - } - } - } - }; - - spyHpm.post.and.returnValue(Promise.resolve(testData.response)); - - // Act - let inputConfig = utils.assign({}, embeddedVisual.config, visualEmbedConfiguration); - embeddedVisual.config = inputConfig; - embeddedVisual.iframeLoaded = true; - - embeddedVisual.load().then(() => { - // Assert - expect(spyHpm.post).toHaveBeenCalled(); - - let spyArgs = spyHpm.post.calls.mostRecent().args; - expect(spyArgs[0]).toEqual('/report/load'); - expect(spyArgs[1]).toEqual(expectedConfiguration); - expect(spyArgs[2]).toEqual({ - bootstrapped: undefined, - sdkVersion: sdkConfig.default.version, - uid: visualUniqueId, - sdkSessionId: sdkSessionId - }); - expect(spyArgs[3]).toEqual(visualFrame.contentWindow); - done(); - }); - }); - - it('embeddedVisual.getFilters(models.FiltersLevel.Report) sends GET /report/filters', function () { - // Act - embeddedVisual.getFilters(models.FiltersLevel.Report); - - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/filters', { uid: visualUniqueId }, visualFrame.contentWindow); - }); - - it('embeddedVisual.setFilters(filters, models.FiltersLevel.Report) sends PUT /report/filters', function () { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ] - }; - - // Act - embeddedVisual.setFilters(testData.filters, models.FiltersLevel.Report); - - // Assert - expect(spyHpm.put).toHaveBeenCalledWith('/report/filters', testData.filters, { uid: visualUniqueId }, visualFrame.contentWindow); - }); - - it('embeddedVisual.getFilters(models.FiltersLevel.Page) sends GET /report/pages/ReportSection1/filters', function () { - // Act - embeddedVisual.getFilters(models.FiltersLevel.Page); - - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/filters`, { uid: visualUniqueId }, visualFrame.contentWindow); - }); - - it('embeddedVisual.setFilters(filters, models.FiltersLevel.Page) sends PUT /report/pages/ReportSection1/filters', function () { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ], - response: { - body: [] - } - }; - - spyHpm.put.and.returnValue(Promise.resolve(testData.response)); - - // Act - embeddedVisual.setFilters(testData.filters, models.FiltersLevel.Page); - - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/ReportSection1/filters`, testData.filters, { uid: visualUniqueId }, visualFrame.contentWindow); - }); - - it('embeddedVisual.getFilters() sends GET /report/pages/ReportSection1/visuals/VisualContainer1/filters', function () { - // Act - embeddedVisual.getFilters(); - - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals/VisualContainer1/filters`, { uid: visualUniqueId }, visualFrame.contentWindow); - }); - - it('embeddedVisual.setFilters(filters) sends PUT /report/pages/ReportSection1/visuals/VisualContainer1/filters', function () { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ], - response: { - body: [] - } - }; - - spyHpm.put.and.returnValue(Promise.resolve(testData.response)); - - // Act - embeddedVisual.setFilters(testData.filters); - - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals/VisualContainer1/filters`, testData.filters, { uid: visualUniqueId }, visualFrame.contentWindow); - }); - - it('Not supported visual method: getPages', function () { - // Act - const attempt = () => { - embeddedVisual.getPages() - }; - - // Assert - expect(attempt).toThrow(visual.Visual.GetPagesNotSupportedError); - }); - - it('Not supported visual method: setPage', function () { - // Act - const attempt = () => { - embeddedVisual.setPage(null) - }; - - // Assert - expect(attempt).toThrow(visual.Visual.SetPageNotSupportedError); - }); - - describe('getVisualDescriptor', function () { - it('embeddedVisual.getVisualDescriptor() sends GET /report/pages/xyz/visuals', function () { - // Arrange - - // Act - embeddedVisual.getVisualDescriptor(); - - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals`, { uid: visualUniqueId }, visualFrame.contentWindow); - }); - - it('embeddedVisual.getVisualDescriptor() returns promise that rejects with server error if there was error getting visual details', function (done) { - // Arrange - const testData = { - expectedError: { - body: { - message: 'internal server error' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - embeddedVisual.getVisualDescriptor() - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals`, { uid: visualUniqueId }, visualFrame.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('embeddedVisual.getVisualDescriptor() returns promise that resolves with visual details', function (done) { - // Arrange - const fakeVisualDescriptor = new visualDescriptor.VisualDescriptor(page1, visualEmbedConfiguration.visualName, 'title', 'type', {}); - const testData = { - expectedResponse: { - body: [fakeVisualDescriptor] - } - }; - - spyHpm.get.and.returnValue(Promise.resolve(testData.expectedResponse)); - - // Act - embeddedVisual.getVisualDescriptor() - .then(visualDescriptor => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/ReportSection1/visuals`, { uid: visualUniqueId }, visualFrame.contentWindow); - expect(visualDescriptor.name).toEqual(fakeVisualDescriptor.name); - done(); - }); - }); - }); - }); - - describe('pages', function () { - it('report.getPages() sends GET /report/pages', function () { - // Arrange - const testData = { - response: { - body: [ - { - name: 'page1' - } - ] - } - }; - - spyHpm.get.and.returnValue(Promise.resolve(testData.response)); - - // Act - report.getPages(); - - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.getPages() return promise that rejects with server error if there was error getting pages', function (done) { - // Arrange - const testData = { - expectedError: { - body: { - message: 'internal server error' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - report.getPages() - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('report.getPages() returns promise that resolves with list of Page objects', function (done) { - // Arrange - const testData = { - pages: [ - 'page1', - 'page2' - ], - expectedResponse: { - body: [ - report.page('page1'), - report.page('page2') - ] - } - }; - - spyHpm.get.and.returnValue(Promise.resolve(testData.expectedResponse)); - - // Act - report.getPages() - .then(pages => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(pages[0].name).toEqual(testData.expectedResponse.body[0].name); - expect(pages[1].name).toEqual(testData.expectedResponse.body[1].name); - done(); - }); - }); - - it('report.getPageByName() returns promise that rejects if report page with given page name not found', function (done) { - // Arrange - const pageName = 'page1'; - const testData = { - expectedError: { - body: { - message: 'page not found' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - report.getPageByName(pageName) - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('report.getPageByName(pageName) returns promise that resolves with page if request is successful', function (done) { - // Arrange - const pageName = "page1"; - const testData = { - expectedResponse: - { - report: report, - name: "page1", - displayName: "Page 1", - isActive: true - } - }; - - spyApp.getPageByName.and.returnValue(Promise.resolve(testData.expectedResponse)); - - // Act - spyApp.getPageByName(pageName) - .then(page => { - // Assert - expect(spyApp.getPageByName).toHaveBeenCalled(); - expect(page.name).toEqual(testData.expectedResponse.name); - expect(page.isActive).toEqual(testData.expectedResponse.isActive); - done(); - }); - }); - - it('report.getActivePage() sends GET /report/pages', function () { - // Arrange - const testData = { - response: { - body: [ - { - name: 'page1' - } - ] - } - }; - - spyHpm.get.and.returnValue(Promise.resolve(testData.response)); - - // Act - report.getActivePage(); - - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.getActivePage() return promise that rejects with server error if there was error getting active page', function (done) { - // Arrange - const testData = { - expectedError: { - body: { - message: 'internal server error' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - report.getActivePage() - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('report.getActivePage() return promise that rejects if embedded report is an RDL report', function (done) { - // Arrange - const testData = { - expectedError: { - body: { - message: APINotSupportedForRDLError - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - report.getActivePage() - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('report.getActivePage() returns promise that resolves with a page if request is successful', function (done) { - // Arrange - const testData = { - expectedResponse: - { - report: report, - name: "page1", - displayName: "Page 1", - isActive: true - } - }; - - spyApp.getActivePage.and.returnValue(Promise.resolve(testData.expectedResponse)); - - // Act - spyApp.getActivePage() - .then(page => { - // Assert - expect(spyApp.getActivePage).toHaveBeenCalled(); - expect(page.name).toEqual(testData.expectedResponse.name); - expect(page.isActive).toEqual(testData.expectedResponse.isActive); - done(); - }); - }); - - it('report.addPage() sends POST /report/addPage with displayName', function () { - // Arrange - const displayName = "testName"; - const expectedRequest = { - displayName: displayName - }; - const expectedHeaders = { uid: uniqueId }; - - spyHpm.post.and.returnValue(Promise.resolve(page1)); - - // Act - report.addPage(displayName); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/addPage', expectedRequest, expectedHeaders, iframe.contentWindow); - }); - - it('report.renamePage() sends PUT /report/pages/{name} with displayName', function () { - // Arrange - const name = "testName"; - const displayName = "newName"; - const expectedHeaders = { uid: uniqueId }; - const expectedRequest = { - name, - displayName - }; - - spyHpm.put.and.returnValue(Promise.resolve(null)); - - // Act - report.renamePage(name, displayName); - - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${name}/name`, expectedRequest, expectedHeaders, iframe.contentWindow); - }); - - it('report.deletePage() sends DELETE /report/pages/{name}', function () { - // Arrange - const name = "testName"; - const expectedHeaders = { uid: uniqueId }; - - spyHpm.delete.and.returnValue(Promise.resolve(null)); - - // Act - report.deletePage(name); - - expect(spyHpm.delete).toHaveBeenCalledWith(`/report/pages/${name}`, {}, expectedHeaders, iframe.contentWindow); - }); - }); - - describe('filters', function () { - it('report.getFilters() sends GET /report/filters', function () { - // Arrange - const testData = { - response: { - body: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ] - } - }; - - spyHpm.get.and.returnValue(Promise.resolve(testData.response)); - - // Act - report.getFilters(); - - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/filters', { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.getFilters() returns promise that rejects with server error if there was error getting filters', function (done) { - // Arrange - const testData = { - expectedErrors: { - body: [ - { - message: 'target is invalid, missing property x' - } - ] - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedErrors)); - - // Act - report.getFilters() - .catch(errors => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/filters', { uid: uniqueId }, iframe.contentWindow); - expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors.body)); - done(); - }); - }); - - it('report.getFilters() returns promise that resolves with the filters if the request is accepted', function (done) { - // Arrange - const testData = { - response: { - body: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ] - } - }; - - spyHpm.get.and.returnValue(Promise.resolve(testData.response)); - - // Act - report.getFilters() - .then(filters => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/filters', { uid: uniqueId }, iframe.contentWindow); - expect(filters).toEqual(testData.response.body); - done(); - }); - }); - - it('report.setFilters(filters) sends PUT /report/filters', function () { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ] - }; - - // Act - report.setFilters(testData.filters); - - // Assert - expect(spyHpm.put).toHaveBeenCalledWith('/report/filters', testData.filters, { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', function (done) { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ], - expectedErrors: { - body: [ - { - message: 'target is invalid, missing property x' - } - ] - } - }; - - spyHpm.put.and.returnValue(Promise.reject(testData.expectedErrors)); - - // Act - report.setFilters(testData.filters) - .catch(errors => { - // Assert - expect(spyHpm.put).toHaveBeenCalledWith('/report/filters', testData.filters, { uid: uniqueId }, iframe.contentWindow); - expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors.body)); - done(); - }); - }); - - it('report.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', function (done) { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ] - }; - - spyHpm.put.and.returnValue(Promise.resolve(null)); - - // Act - report.setFilters(testData.filters) - .then(response => { - // Assert - expect(spyHpm.put).toHaveBeenCalledWith('/report/filters', testData.filters, { uid: uniqueId }, iframe.contentWindow); - expect(response).toEqual(null); - done(); - }); - }); - - it('report.removeFilters() sends PUT /report/filters', function () { - // Arrange - - // Act - report.removeFilters(); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/filters', { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.removeFilters() returns promise that resolves with null if request is accepted', function (done) { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve(null)); - - // Act - report.removeFilters() - .then(response => { - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/filters', { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, iframe.contentWindow); - expect(response).toEqual(null); - done(); - }); - }); - }); - - describe('switchMode', function () { - it('report.switchMode() sends POST /report/switchMode', function () { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.switchMode(models.ViewMode.Edit); - - // Assert - let url = '/report/switchMode/edit'; - expect(spyHpm.post).toHaveBeenCalledWith(url, null, { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.switchMode() returns promise that resolves if the request is accepted', function (done) { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.switchMode(models.ViewMode.Edit) - .then(() => { - // Assert - let url = '/report/switchMode/edit'; - expect(spyHpm.post).toHaveBeenCalledWith(url, null, { uid: uniqueId }, iframe.contentWindow); - done(); - }); - }); - }); - - describe('save', function () { - it('report.save() sends POST /report/save', function () { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.save(); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/save', null, { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.save() returns promise that resolves if the request is accepted', function (done) { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.save() - .then(() => { - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/save', null, { uid: uniqueId }, iframe.contentWindow); - done(); - }); - }); - }); - - describe('switchLayout', function () { - it('report.switchLayout(layout) returns promise that rejects with errors if there was error if initial layout and current layout type do not match', function (done) { - // Arrange - // Set initial layout to desktop layout - report.config.settings.layoutType = models.LayoutType.Master; - - const layoutType = models.LayoutType.MobileLandscape; - const testData = { - expectedError: { - message: 'Switching between mobile and desktop layouts is not supported. Please reset the embed container and re-embed with required layout.' - }, - settings: { - layoutType: layoutType - } - }; - - spyHpm.patch.and.returnValues(Promise.reject(testData.expectedError)); - - // Act - report.switchLayout(layoutType) - .catch(error => { - // Assert - expect(spyHpm.patch).not.toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.message); - done(); - }); - }); - - it('report.switchLayout(layout) returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - // Set initial layout to mobile layout - report.config.settings.layoutType = models.LayoutType.MobilePortrait; - - const layoutType = models.LayoutType.MobileLandscape; - - spyApp.switchLayout.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.switchLayout(layoutType) - .then(response => { - // Assert - expect(spyApp.switchLayout).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - }); - - describe('saveAs', function () { - let saveAsParameters: models.ISaveAsParameters = { name: "reportName" }; - - it('report.saveAs() sends POST /report/saveAs', function () { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.saveAs(saveAsParameters); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/saveAs', saveAsParameters, { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.saveAs() returns promise that resolves if the request is accepted', function (done) { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.saveAs(saveAsParameters) - .then(() => { - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/saveAs', saveAsParameters, { uid: uniqueId }, iframe.contentWindow); - done(); - }); - }); - }); - - describe('setAccessToken', function () { - let accessToken: string = "fakeToken"; - - it('report.setAccessToken() sends POST /report/token', function () { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.setAccessToken(accessToken); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/token', accessToken, { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.setAccessToken() returns promise that resolves if the request is accepted', function (done) { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - let newToken = "newToken" - // Act - report.setAccessToken(newToken) - .then(() => { - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/token', newToken, { uid: uniqueId }, iframe.contentWindow); - expect(report.service.accessToken).toEqual(newToken); - expect(report.config.accessToken).toEqual(newToken); - expect(report.element.getAttribute(embed.Embed.accessTokenAttribute)).toEqual(newToken); - done(); - }); - }); - }); - - describe('print', function () { - it('report.print() sends POST /report/print', function () { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.print(); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/print', null, { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.print() returns promise that resolves if the request is accepted', function (done) { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.print() - .then(() => { - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/print', null, { uid: uniqueId }, iframe.contentWindow); - done(); - }); - }); - }); - - describe('reload', function () { - it('report.reload() sends POST /report/load with configuration in body', function (done) { - // Arrange - const testData = { - loadConfiguration: { - id: 'fakeId', - accessToken: 'fakeToken' - }, - response: { - body: null - } - }; - - spyHpm.post.and.returnValue(Promise.resolve(testData.response)); - let expectedConfiguration = utils.assign({}, report.config, testData.loadConfiguration); - report.config = expectedConfiguration; - report.load() - .then(() => { - spyHpm.post.calls.reset(); - - // Act - report.reload(); - - const expectedHeaders = { - bootstrapped: undefined, - sdkVersion: sdkConfig.default.version, - uid: uniqueId, - sdkSessionId: sdkSessionId - }; - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/load', expectedConfiguration, expectedHeaders, iframe.contentWindow); - done(); - }); - }); - }); - - describe('refresh', function () { - it('report.refresh() sends POST /report/refresh', function () { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve({ - body: {} - })); - - // Act - report.refresh(); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/refresh', null, { uid: uniqueId }, iframe.contentWindow); - }); - }); - - describe('settings', function () { - it('report.updateSettings(settings) sends PATCH /report/settings with settings object', function () { - // Arrange - const testData = { - settings: { - filterPaneEnabled: false - } - }; - - // Act - report.updateSettings(testData.settings); - - // Assert - expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - }); - - it('report.updateSettings(setting) returns promise that rejects with validation error if object is invalid', function (done) { - // Arrange - const testData = { - settings: { - filterPaneEnabled: false - }, - expectedError: { - body: [ - { - message: 'settings object is invalid' - } - ] - } - }; - - spyHpm.patch.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - report.updateSettings(testData.settings) - .catch(errors => { - - // Assert - expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(errors).toEqual(testData.expectedError.body); - done() - }); - }); - - it('report.updateSettings(settings) returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const testData = { - settings: { - filterPaneEnabled: false - } - }; - - spyHpm.patch.and.returnValue(Promise.resolve(null)); - - // Act - report.updateSettings(testData.settings) - .then(response => { - - // Assert - expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(response).toEqual(null); - done() - }); - }); - - it('report.addContextMenuCommand(commandName, commandTitle, contextMenuTitle) returns promise that rejects with validation errors if extensions property is invalid', function (done) { - // Arrange - const commandName = "name1"; - const commandTitle = "title1"; - const contextMenuTitle = "menu1"; - const testData = { - expectedError: { - body: [ - { - message: 'extensions property is invalid' - } - ] - }, - settings: { - extensions: { - commands: [{ - name: "name1", - title: "title1", - extend: { - visualContextMenu: { - title: contextMenuTitle, - menuLocation: 0 - } - } - }], - groups: [] - } - } - }; - - spyHpm.patch.and.returnValues(Promise.reject(testData.expectedError)); - - // Act - report.addContextMenuCommand(commandName, commandTitle, contextMenuTitle, 0, "", "", "") - .catch(error => { - // Assert - expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(jasmine.objectContaining(testData.expectedError.body)); - done(); - }); - }); - - it('report.addContextMenuCommand(commandName, commandTitle, contextMenuTitle) returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const commandName = "name2"; - const commandTitle = "title2"; - const contextMenuTitle = "menu2"; - - spyApp.addContextMenuCommand.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.addContextMenuCommand(commandName, commandTitle, contextMenuTitle) - .then(response => { - // Assert - expect(spyApp.addContextMenuCommand).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('report.addOptionsMenuCommand(commandName, commandTitle, optionsMenuTitle) returns promise that rejects with validation errors if extensions property is invalid', function (done) { - // Arrange - const commandName = "name1"; - const commandTitle = "title1"; - const optionsMenuTitle = "menu1"; - const testData = { - expectedError: { - body: [ - { - message: 'extensions property is invalid' - } - ] - }, - settings: { - extensions: { - commands: [{ - name: "name1", - title: "title1", - extend: { - visualOptionsMenu: { - title: "menu1", - menuLocation: 0, - } - }, - icon: undefined - }], - groups: [] - } - } - }; - - spyHpm.patch.and.returnValues(Promise.reject(testData.expectedError)); - - // Act - report.addOptionsMenuCommand(commandName, commandTitle, optionsMenuTitle) - .catch(error => { - // Assert - expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(jasmine.objectContaining(testData.expectedError.body)); - done(); - }); - }); - - it('report.addOptionsMenuCommand(commandName, commandTitle, optionsMenuTitle) returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const commandName = "name2"; - const commandTitle = "title2"; - const optionsMenuTitle = "menu2"; - - spyApp.addOptionsMenuCommand.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.addOptionsMenuCommand(commandName, commandTitle, optionsMenuTitle) - .then(response => { - // Assert - expect(spyApp.addOptionsMenuCommand).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('report.removeContextMenuCommand(commandName) returns promise that rejects with validation errors if command name is invalid', function (done) { - // Arrange - const commandName = "name1"; - const testData = { - expectedError: { - message: 'PowerBIEntityNotFound' - }, - settings: { - extensions: { - commands: [{ - name: "name1", - title: "title1", - extend: { - } - }] - } - } - }; - - spyApp.removeContextMenuCommand.and.returnValues(Promise.reject(testData.expectedError)); - - // Act - spyApp.removeContextMenuCommand(commandName) - .catch(error => { - // Assert - expect(spyHpm.patch).not.toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); - done(); - }); - }); - - it('report.removeContextMenuCommand(commandName) returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const commandName = "name2"; - - spyApp.removeContextMenuCommand.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.removeContextMenuCommand(commandName) - .then(response => { - // Assert - expect(spyApp.removeContextMenuCommand).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('report.removeOptionsMenuCommand(commandName) returns promise that rejects with validation errors if command name is invalid', function (done) { - // Arrange - const commandName = "name1"; - const testData = { - expectedError: { - message: 'PowerBIEntityNotFound' - }, - settings: { - extensions: { - commands: [{ - name: "name1", - title: "title1", - icon: "", - extend: { - } - }] - } - } - }; - - spyApp.removeOptionsMenuCommand.and.returnValues(Promise.reject(testData.expectedError)); - - // Act - spyApp.removeOptionsMenuCommand(commandName) - .catch(error => { - // Assert - expect(spyHpm.patch).not.toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); - done(); - }); - }); - - it('report.removeOptionsMenuCommand(commandName) returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const commandName = "name2"; - - spyApp.removeOptionsMenuCommand.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.removeOptionsMenuCommand(commandName) - .then(response => { - // Assert - expect(spyApp.removeOptionsMenuCommand).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('report.setVisualDisplayState(pageName, visualName, displayState) returns promise that rejects with validation error if display state is invalid', function (done) { - // Arrange - const pageName = 'page1'; - const visualName = 'visual'; - const displayState = 2; - const testData = { - expectedError: { - body: { - message: 'display state is invalid' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - report.setVisualDisplayState(pageName, visualName, displayState) - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('report.setVisualDisplayState(pageName, visualName, displayState) returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const pageName = 'page1'; - const visualName = 'visual'; - const displayState = models.VisualContainerDisplayMode.Visible; - - spyApp.setVisualDisplayState.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.setVisualDisplayState(pageName, visualName, displayState) - .then(response => { - // Assert - expect(spyApp.setVisualDisplayState).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('report.resizeVisual returns promise that rejects with validation error if page name is invalid', function (done) { - // Arrange - const pageName = 'invalid page'; - const visualName = 'visual'; - const width = 200; - const height = 100; - const testData = { - expectedError: { - body: { - message: 'page name is invalid' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - report.resizeVisual(pageName, visualName, width, height) - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('report.resizeVisual returns promise that resolves with null if request is valid and accepted', function (done) { - // Arrange - const pageName = 'page1'; - const visualName = 'visual'; - const width = 200; - const height = 100; - - spyApp.resizeVisual.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.resizeVisual(pageName, visualName, width, height) - .then(response => { - // Assert - expect(spyApp.resizeVisual).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('report.resizeActivePage returns promise that rejects with validation error if page size type is invalid', function (done) { - // Arrange - const pageSizeType = 5; - const width = 200; - const height = 100; - const testData = { - expectedError: { - body: { - message: 'page size type is invalid' - } - }, - settings: { - layoutType: models.LayoutType.Custom, - customLayout: { - pageSize: { - type: pageSizeType, - width: width, - height: height - } - } - } - }; - - spyHpm.patch.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - report.resizeActivePage(pageSizeType, width, height) - .catch(error => { - // Assert - expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('report.resizeActivePage returns promise that resolves with null if request is valid and accepted', function (done) { - // Arrange - const pageSizeType = models.PageSizeType.Custom; - const width = 200; - const height = 100; - - spyApp.resizeActivePage.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.resizeActivePage(pageSizeType, width, height) - .then(response => { - // Assert - expect(spyApp.resizeActivePage).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('moveVisual returns promise that rejects with validation error if visual name is invalid', function (done) { - // Arrange - const pageName = 'page1'; - const visualName = 'invalid visual'; - const x = 0; - const y = 0; - const testData = { - expectedError: { - body: { - message: 'visual name is invalid' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - report.moveVisual(pageName, visualName, x, y) - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('moveVisual returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const pageName = 'page1'; - const visualName = 'visual'; - const x = 0; - const y = 0; - - spyApp.moveVisual.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.moveVisual(pageName, visualName, x, y) - .then(response => { - // Assert - expect(spyApp.moveVisual).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - }); - }); - - describe('create', function () { - describe('createReport', function () { - it('create.createReport() sends POST /report/create with configuration in body', function () { - // Arrange - const testData = { - createConfiguration: { - datasetId: 'fakeId', - accessToken: 'fakeToken' - }, - response: { - body: null - } - }; - - spyHpm.post.and.returnValue(Promise.resolve(testData.response)); - - // Act - create.createReport(testData.createConfiguration); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/create', testData.createConfiguration, { uid: createUniqueId, sdkSessionId: sdkSessionId }, createIframe.contentWindow); - }); - - it('create.createReport() returns promise that rejects with validation error if the create configuration is invalid', function (done) { - // Arrange - const testData = { - createConfiguration: { - datasetId: 'fakeId', - accessToken: 'fakeToken' - }, - errorResponse: { - body: { - message: "invalid configuration object" - } - } - }; - - spyHpm.post.and.returnValue(Promise.reject(testData.errorResponse)); - - // Act - create.createReport(testData.createConfiguration) - .catch(error => { - expect(spyHpm.post).toHaveBeenCalledWith('/report/create', testData.createConfiguration, { uid: createUniqueId, sdkSessionId: sdkSessionId }, createIframe.contentWindow); - expect(error).toEqual(testData.errorResponse.body); - // Assert - done(); - }); - }); - - it('create.createReport() returns promise that resolves with null if create report was successful', function (done) { - // Arrange - const testData = { - createConfiguration: { - datasetId: 'fakeId', - accessToken: 'fakeToken' - }, - response: { - body: null - } - }; - - spyHpm.post.and.returnValue(Promise.resolve(testData.response)); - - // Act - create.createReport(testData.createConfiguration) - .then(response => { - expect(spyHpm.post).toHaveBeenCalledWith('/report/create', testData.createConfiguration, { uid: createUniqueId, sdkSessionId: sdkSessionId }, createIframe.contentWindow); - expect(response).toEqual(null); - // Assert - done(); - }); - }); - }); - }); - - describe('dashboard', function () { - describe('load', function () { - it('dashboard.load() sends POST /dashboard/load with configuration in body', function () { - // Arrange - const testData = { - loadConfiguration: { - id: 'fakeId', - accessToken: 'fakeToken', - type: 'dashboard' - }, - response: { - body: null - } - }; - - spyHpm.post.and.returnValue(Promise.resolve(testData.response)); - - // Act - let expectedConfiguration = utils.assign({}, dashboard.config, testData.loadConfiguration); - dashboard.config = expectedConfiguration; - dashboard.load(); - - const expectedHeaders = { - bootstrapped: undefined, - sdkVersion: sdkConfig.default.version, - uid: dashboardUniqueId, - sdkSessionId: sdkSessionId - }; - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/dashboard/load', expectedConfiguration, expectedHeaders, dashboardIframe.contentWindow); - }); - }); - }); - - describe('page', function () { - describe('filters', function () { - it('page.getFilters() sends GET /report/pages/xyz/filters', function () { - // Arrange - - // Act - page1.getFilters(); - - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { uid: uniqueId }, iframe.contentWindow); - }); - - it('page.getFilters() return promise that rejects with server error if there was error getting filters', function (done) { - // Arrange - const testData = { - expectedError: { - body: { - message: 'internal server error' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - page1.getFilters() - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('page.getFilters() returns promise that resolves with list of filters', function (done) { - // Arrange - const testData = { - expectedResponse: { - body: [ - { x: 'fakeFilter1' }, - { x: 'fakeFilter2' } - ] - } - }; - - spyHpm.get.and.returnValue(Promise.resolve(testData.expectedResponse)); - - // Act - page1.getFilters() - .then(filters => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { uid: uniqueId }, iframe.contentWindow); - // @ts-ignore as testData is not of type IFilter - expect(filters).toEqual(testData.expectedResponse.body); - done(); - }); - }); - - it('page.setFilters(filters) sends PUT /report/pages/xyz/filters', function () { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ], - response: { - body: [] - } - }; - - spyHpm.put.and.returnValue(Promise.resolve(testData.response)); - - // Act - page1.setFilters(testData.filters); - - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, testData.filters, { uid: uniqueId }, iframe.contentWindow); - }); - - it('page.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', function (done) { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ], - expectedErrors: { - body: [ - { - message: 'target is invalid, missing property x' - } - ] - } - }; - - spyHpm.put.and.returnValue(Promise.reject(testData.expectedErrors)); - - // Act - page1.setFilters(testData.filters) - .catch(errors => { - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, testData.filters, { uid: uniqueId }, iframe.contentWindow); - expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors.body)); - done(); - }); - }); - - it('page.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', function (done) { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ] - }; - - spyHpm.put.and.returnValue(Promise.resolve(null)); - - // Act - page1.setFilters(testData.filters) - .then(response => { - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, testData.filters, { uid: uniqueId }, iframe.contentWindow); - expect(response).toEqual(null); - done(); - }); - }); - - it('page.removeFilters() sends PUT /report/pages/xyz/filters', function () { - // Arrange - - // Act - page1.removeFilters(); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, iframe.contentWindow); - }); - - it('page.removeFilters() returns promise that resolves with null if request is accepted', function (done) { - // Arrange - spyHpm.post.and.returnValue(Promise.resolve(null)); - - // Act - page1.removeFilters() - .then(response => { - // Assert - expect(spyHpm.post).toHaveBeenCalledWith(`/report/pages/${page1.name}/filters`, { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, iframe.contentWindow); - expect(response).toEqual(null); - done(); - }); - }); - }); - - describe('setActive', function () { - it('page.setActive() sends PUT /report/pages/active', function () { - // Arrange - const testData = { - page: { - name: page1.name, - displayName: null, - isActive: true, - } - }; - - spyHpm.put.and.returnValue(Promise.resolve(null)); - - // Act - page1.setActive(); - - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/active`, testData.page, { uid: uniqueId }, iframe.contentWindow); - }); - - it('page.setActive() returns a promise rejected with errors if the page was invalid', function (done) { - // Arrange - const testData = { - page: { - name: page1.name, - displayName: null, - isActive: true, - }, - response: { - body: [ - { - message: 'page abc123 does not exist on report xyz' - } - ] - } - }; - - spyHpm.put.and.returnValue(Promise.reject(testData.response)); - - // Act - page1.setActive() - .catch(errors => { - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/active`, testData.page, { uid: uniqueId }, iframe.contentWindow); - expect(errors).toEqual(jasmine.objectContaining(testData.response.body)); - done(); - }); - }); - - it('page.setActive() returns a promise resolved with null if the page is valid', function (done) { - // Arrange - const testData = { - page: { - name: page1.name, - displayName: null, - isActive: true, - } - }; - - spyHpm.put.and.returnValue(Promise.resolve(null)); - - // Act - page1.setActive() - .then(response => { - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/active`, testData.page, { uid: uniqueId }, iframe.contentWindow); - expect(response).toEqual(null); - done(); - }); - }); - }); - describe('custom layout', function () { - it('page.setVisualDisplayState returns promise that rejects with validation error if display state is invalid', function (done) { - // Arrange - const visualName = 'visual'; - const displayState = 2; - const testData = { - expectedError: { - body: { - message: 'display state is invalid' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - page1.setVisualDisplayState(visualName, displayState) - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('page.setVisualDisplayState returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const visualName = 'visual'; - const displayState = models.VisualContainerDisplayMode.Visible; - - spyApp.setVisualDisplayState.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.setVisualDisplayState(visualName, displayState) - .then(response => { - // Assert - expect(spyApp.setVisualDisplayState).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('page.moveVisual returns promise that rejects with validation error if visual name is invalid', function (done) { - // Arrange - const visualName = 'invalid visual'; - const x = 0; - const y = 0; - const testData = { - expectedError: { - body: { - message: 'visual name is invalid' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - page1.moveVisual(visualName, x, y) - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('page.moveVisual returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const visualName = 'visual'; - const x = 0; - const y = 0; - - spyApp.moveVisual.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.moveVisual(visualName, x, y) - .then(response => { - // Assert - expect(spyApp.moveVisual).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('page.resizePage returns promise that rejects with validation error if page is not active page', function (done) { - // Arrange - const pageSizeType = 1; - const width = 200; - const height = 100; - const testData = { - expectedError: { - message: 'Cannot resize the page. Only the active page can be resized' - }, - settings: { - layoutType: models.LayoutType.Custom, - customLayout: { - pageSize: { - type: pageSizeType, - width: width, - height: height - } - } - } - }; - - spyHpm.patch.and.returnValue(Promise.reject(testData.expectedError.message)); - - // Act - page1.resizePage(pageSizeType, width, height) - .catch(error => { - // Assert - expect(spyHpm.patch).not.toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.message); - done(); - }); - }); - - it('page.resizePage returns promise that resolves with null if page is active page', function (done) { - // Arrange - const pageSizeType = 1; - const width = 200; - const height = 100; - const testData = { - settings: { - layoutType: models.LayoutType.Custom, - customLayout: { - pageSize: { - type: pageSizeType, - width: width, - height: height - } - } - } - }; - - page1.isActive = true; - spyHpm.patch.and.returnValue(Promise.resolve(null)); - - // Act - page1.resizePage(pageSizeType, width, height) - .then(response => { - // Assert - expect(spyHpm.patch).toHaveBeenCalledWith('/report/settings', testData.settings, { uid: uniqueId }, iframe.contentWindow); - expect(response).toEqual(null); - done(); - }); - }); - - it('page.resizePage returns promise that resolves with null if request is valid and accepted', function (done) { - // Arrange - const pageSizeType = models.PageSizeType.Custom; - const width = 200; - const height = 100; - - spyApp.resizeActivePage.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.resizeActivePage(pageSizeType, width, height) - .then(response => { - // Assert - expect(spyApp.resizeActivePage).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - }); - }); - - describe('setDisplayName', function () { - it('page.setDisplayName(displayName) sends PUT /report/pages/{pageName}/name', function () { - // Arrange - const displayName = "newName"; - const testData = { - page: { - name: page1.name, - displayName, - } - }; - - spyHpm.put.and.returnValue(Promise.resolve(null)); - - // Act - page1.setDisplayName(displayName); - - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/name`, testData.page, { uid: uniqueId }, iframe.contentWindow); - }); - }); - - describe('getVisualByName', function () { - it('page.getVisualByName(visualName) returns promise that rejects if visual with given name not found', function (done) { - // Arrange - const pageName = page1.name; - const visualName = "visual1"; - const testData = { - expectedError: { - body: { - message: 'visual not found' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - // Act - page1.getVisualByName(visualName) - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${pageName}/visuals`, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('page.getVisualByName(visualName) returns promise that resolves with visual if request is successful', function (done) { - // Arrange - const pageName = page1.name; - const visualName = "visual1"; - const testData = { - expectedResponse: - { - name: "visual1", - title: "Visual 1", - type: "type1", - layout: {}, - page: {} - } - }; - - spyApp.getVisualByName.and.returnValue(Promise.resolve(testData.expectedResponse)); - - // Act - spyApp.getVisualByName(visualName) - .then(visual => { - // Assert - expect(spyApp.getVisualByName).toHaveBeenCalled(); - expect(visual.name).toEqual(testData.expectedResponse.name); - expect(visual.title).toEqual(testData.expectedResponse.title); - done(); - }); - }); - }); - - describe('visual', function () { - describe('filters', function () { - it('visual.getFilters() sends GET /report/pages/xyz/visuals/uvw/filters', function () { - // Arrange - - // Act - visual1.getFilters(); - - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { uid: uniqueId }, iframe.contentWindow); - }); - - it('visual.getFilters() return promise that rejects with server error if there was error getting filters', function (done) { - // Arrange - const testData = { - expectedError: { - body: { - message: 'internal server error' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - visual1.getFilters() - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('visual.getFilters() returns promise that resolves with list of filters', function (done) { - // Arrange - const testData = { - expectedResponse: { - body: [ - { x: 'fakeFilter1' }, - { x: 'fakeFilter2' } - ] - } - }; - - spyHpm.get.and.returnValue(Promise.resolve(testData.expectedResponse)); - - // Act - visual1.getFilters() - .then(filters => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { uid: uniqueId }, iframe.contentWindow); - // @ts-ignore as testData is not of type IFilter - expect(filters).toEqual(testData.expectedResponse.body); - done(); - }); - }); - - it('visual.setFilters(filters) sends PUT /report/pages/xyz/visuals/uvw/filters', function () { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ], - response: { - body: [] - } - }; - - spyHpm.put.and.returnValue(Promise.resolve(testData.response)); - - // Act - visual1.setFilters(testData.filters); - - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, testData.filters, { uid: uniqueId }, iframe.contentWindow); - }); - - it('visual.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', function (done) { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ], - expectedErrors: { - body: [ - { - message: 'target is invalid, missing property x' - } - ] - } - }; - - spyHpm.put.and.returnValue(Promise.reject(testData.expectedErrors)); - - // Act - visual1.setFilters(testData.filters) - .catch(errors => { - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, testData.filters, { uid: uniqueId }, iframe.contentWindow); - expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors.body)); - done(); - }); - }); - - it('visual.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', function (done) { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "Cars", measure: "Make" }, "In", ["subaru", "honda"])).toJSON(), - (new models.AdvancedFilter({ table: "Cars", measure: "Make" }, "And", [{ value: "subaru", operator: "None" }, { value: "honda", operator: "Contains" }])).toJSON() - ] - }; - - spyHpm.put.and.returnValue(Promise.resolve(null)); - - // Act - visual1.setFilters(testData.filters) - .then(response => { - // Assert - expect(spyHpm.put).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, testData.filters, { uid: uniqueId }, iframe.contentWindow); - expect(response).toEqual(null); - done(); - }); - }); - - it('visual.removeFilters() sends PUT /report/pages/xyz/visuals/uvw/filters', function () { - // Arrange - - // Act - visual1.removeFilters(); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, iframe.contentWindow); - }); - - it('visual.removeFilters() returns promise that resolves with null if request is accepted', function (done) { - // Arrange - spyHpm.put.and.returnValue(Promise.resolve(null)); - - // Act - visual1.removeFilters() - .then(response => { - // Assert - expect(spyHpm.post).toHaveBeenCalledWith(`/report/pages/${page1.name}/visuals/${visual1.name}/filters`, { filtersOperation: models.FiltersOperations.RemoveAll, filters: undefined }, { uid: uniqueId }, iframe.contentWindow); - expect(response).toEqual(null); - done(); - }); - }); - }); - - describe('custom layout', function () { - it('visual.moveVisual() returns promise that rejects with server error if error in updating setting', function (done) { - // Arrange - const x = 0; - const y = 0; - const testData = { - expectedError: { - body: { - message: 'internal server error' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - visual1.moveVisual(x, y) - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('visual.moveVisual() returns promise that resolves with null if request is valid and accepted', function (done) { - // Arrange - const x = 0; - const y = 0; - - spyApp.moveVisual.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.moveVisual(x, y) - .then(response => { - // Assert - expect(spyApp.moveVisual).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('visual.setVisualDisplayState(displayState) returns promise that rejects with validation error if display state is invalid', function (done) { - // Arrange - const displayState = 2; - const testData = { - expectedError: { - body: { - message: 'mode property is invalid' - } - }, - }; - - spyApp.setVisualDisplayState.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - spyApp.setVisualDisplayState(displayState) - .catch(error => { - // Assert - expect(error).toEqual(testData.expectedError); - done(); - }); - }); - - it('visual.setVisualDisplayState(displayState) returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const displayState = models.VisualContainerDisplayMode.Visible; - - spyApp.setVisualDisplayState.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.setVisualDisplayState(displayState) - .then(response => { - // Assert - expect(spyApp.setVisualDisplayState).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - - it('visual.resizeVisual returns promise that rejects with server error if error in updating setting', function (done) { - // Arrange - const width = 200; - const height = 100; - const testData = { - expectedError: { - body: { - message: 'internal server error' - } - } - }; - - spyHpm.get.and.returnValue(Promise.reject(testData.expectedError)); - - // Act - visual1.resizeVisual(width, height) - .catch(error => { - // Assert - expect(spyHpm.get).toHaveBeenCalledWith('/report/pages', { uid: uniqueId }, iframe.contentWindow); - expect(error).toEqual(testData.expectedError.body); - done(); - }); - }); - - it('visual.resizeVisual returns promise that resolves with null if request is valid and accepted', function (done) { - // Arrange - const width = 200; - const height = 100; - - spyApp.resizeVisual.and.returnValue(Promise.resolve(null)); - - // Act - spyApp.resizeVisual(width, height) - .then(response => { - // Assert - expect(spyApp.resizeVisual).toHaveBeenCalled(); - expect(response).toEqual(null); - done(); - }); - }); - }); - - describe('theme', function () { - it('report.applyTheme(theme) sends PUT /report/theme with theme in body', function () { - // Arrange - const testData = { - theme: { - themeJson: { - name: "Theme ABC 123" - } - }, - response: { - body: null - } - }; - - const expectedHeaders = { - bootstrapped: undefined, - sdkVersion: sdkConfig.default.version, - uid: uniqueId, - sdkSessionId: sdkSessionId - }; - - spyHpm.post.and.returnValue(Promise.resolve(testData.response)); - report.applyTheme(testData.theme) - .then(() => { - spyHpm.post.calls.reset(); - - // Act - report.reload(); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/load', jasmine.objectContaining(testData.theme), expectedHeaders, iframe.contentWindow); - }); - }); - - it('report.resetTheme() sends PUT /report/theme with empty object as theme in body', function () { - // Arrange - const response = { - body: null - }; - - const expectedHeaders = { - bootstrapped: undefined, - sdkVersion: sdkConfig.default.version, - uid: uniqueId, - sdkSessionId: sdkSessionId - }; - - spyHpm.post.and.returnValue(Promise.resolve(response)); - report.resetTheme() - .then(() => { - spyHpm.post.calls.reset(); - - // Act - report.reload(); - - // Assert - expect(spyHpm.post).toHaveBeenCalledWith('/report/load', jasmine.objectContaining({}), expectedHeaders, iframe.contentWindow); - }); - }); - }); - }); - - describe('SDK-to-Router (Event subscription)', function () { - /** - * This test should likely be moved to mock app section or removed since it is already covered. - * The validation of supported events should likely happen by powerbi instead of by the SDK - * since this is maitanence problem - */ - it(`report.on(eventName, handler) should throw error if eventName is not supported`, function () { - // Arrange - const testData = { - eventName: 'xyz', - handler: jasmine.createSpy('handler') - }; - - // Act - const attemptToSubscribeToEvent = () => { - report.on(testData.eventName, testData.handler); - }; - - // Assert - expect(attemptToSubscribeToEvent).toThrowError(); - }); - }); -}); - -describe('SDK-to-WPMP', function () { - let $element: JQuery; - let iframe: HTMLIFrameElement; - let powerbi: service.Service; - let report: report.Report; - let uniqueId: string; - - beforeAll(function () { - const spyWpmpFactory: factories.IWpmpFactory = (name?: string, logMessages?: boolean) => { - return spyWpmp; - }; - - powerbi = new service.Service(factories.hpmFactory, spyWpmpFactory, factories.routerFactory); - - $element = $(`
`) - .appendTo(document.body); - - const iframeSrc = "base/test/utility/noop.html"; - const embedConfiguration = { - type: "report", - id: "fakeReportId", - accessToken: 'fakeToken', - embedUrl: iframeSrc, - wpmpName: 'SDK-to-WPMP report wpmp' - }; - report = powerbi.embed($element[0], embedConfiguration); - uniqueId = report.config.uniqueId; - - iframe = $element.find('iframe')[0]; - - // Reset load handler - spyWpmp.addHandler.calls.reset(); - spyHpm.post.calls.reset(); - }); - - afterAll(function () { - powerbi.reset($element.get(0)); - $element.remove(); - powerbi.wpmp.stop(); - }); - - afterEach(function () { - spyHpm.get.calls.reset(); - spyHpm.post.calls.reset(); - spyHpm.patch.calls.reset(); - spyHpm.put.calls.reset(); - spyHpm.delete.calls.reset(); - - spyRouter.get.calls.reset(); - spyRouter.post.calls.reset(); - spyRouter.patch.calls.reset(); - spyRouter.put.calls.reset(); - spyRouter.delete.calls.reset(); - }); - - describe('Event handlers', function () { - it(`handler passed to report.on(eventName, handler) is called when POST /report/:uniqueId/events/:eventName is received`, function () { - // Arrange - const testData = { - eventName: 'filtersApplied', - handler: jasmine.createSpy('handler'), - filtersAppliedEvent: { - data: { - method: 'POST', - url: `/reports/${uniqueId}/events/filtersApplied`, - body: { - initiator: 'sdk', - filters: [ - { - x: 'fakeFilter' - } - ] - } - } - } - }; - - report.on(testData.eventName, testData.handler); - - // Act - spyWpmp.onMessageReceived(testData.filtersAppliedEvent); - - // Assert - expect(testData.handler).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.filtersAppliedEvent.data.body })); - }); - - it(`off('eventName', handler) will remove single handler which matches function reference for that event`, function () { - // Arrange - const testData = { - eventName: 'filtersApplied', - handler: jasmine.createSpy('handler1'), - simulatedEvent: { - data: { - method: 'POST', - url: `/reports/${uniqueId}/events/filtersApplied`, - body: { - initiator: 'sdk', - filter: { - x: '1', - y: '2' - } - } - } - } - }; - - report.on(testData.eventName, testData.handler); - report.off(testData.eventName, testData.handler); - - // Act - spyWpmp.onMessageReceived(testData.simulatedEvent); - - // Assert - expect(testData.handler).not.toHaveBeenCalled(); - }); - - it('if multiple handlers for the same event are registered they will all be called', function () { - // Arrange - const testData = { - eventName: 'filtersApplied', - handler: jasmine.createSpy('handler1'), - handler2: jasmine.createSpy('handler2'), - handler3: jasmine.createSpy('handler3'), - simulatedEvent: { - data: { - method: 'POST', - url: `/reports/${uniqueId}/events/filtersApplied`, - body: { - initiator: 'sdk', - filter: { - x: '1', - y: '2' - } - } - } - } - }; - - report.on(testData.eventName, testData.handler); - report.on(testData.eventName, testData.handler2); - report.on(testData.eventName, testData.handler3); - - // Act - spyWpmp.onMessageReceived(testData.simulatedEvent); - - // Assert - expect(testData.handler).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedEvent.data.body })); - expect(testData.handler2).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedEvent.data.body })); - expect(testData.handler3).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedEvent.data.body })); - }); - - - it(`off('eventName') will remove all handlers which matches event name`, function () { - // Arrange - const testData = { - eventName: 'filtersApplied', - handler: jasmine.createSpy('handler1'), - handler2: jasmine.createSpy('handler2'), - handler3: jasmine.createSpy('handler3'), - simulatedEvent: { - data: { - method: 'POST', - url: '/reports/fakeReportId/events/filtersApplied', - body: { - initiator: 'sdk', - filter: { - x: '1', - y: '2' - } - } - } - } - }; - - report.on(testData.eventName, testData.handler); - report.on(testData.eventName, testData.handler2); - report.on(testData.eventName, testData.handler3); - report.off(testData.eventName); - - // Act - spyWpmp.onMessageReceived(testData.simulatedEvent); - - // Assert - expect(testData.handler).not.toHaveBeenCalled(); - expect(testData.handler2).not.toHaveBeenCalled(); - expect(testData.handler3).not.toHaveBeenCalled(); + describe('exitFullscreen', function () { + it('clears the iframe fullscreen element', function () { + let requestFullscreenSpy = jasmine.createSpy(); + iframe.requestFullscreen = requestFullscreenSpy; + let report = powerbi.get(container); + report.fullscreen(); + report.exitFullscreen(); + expect(requestFullscreenSpy).toHaveBeenCalled(); }); }); }); - -describe('SDK-to-MockApp', function () { - let $element: JQuery; - let $element2: JQuery; - let iframe: HTMLIFrameElement; - let iframe2: HTMLIFrameElement; - let iframeHpm: Hpm.HttpPostMessage; - let iframeHpm2: Hpm.HttpPostMessage; - let iframeLoaded: Promise; - let powerbi: service.Service; - let report: report.Report; - let page1: page.Page; - let report2: report.Report; - - beforeAll(function () { - powerbi = new service.Service(factories.hpmFactory, factories.wpmpFactory, factories.routerFactory, { - wpmpName: 'SDK-to-MockApp HostWpmp', - logMessages - }); - - $element = $(`
`) - .appendTo(document.body); - - $element2 = $(`
`) - .appendTo(document.body); - - const iframeSrc = "base/test/utility/noop.html"; - const embedConfiguration: embed.IEmbedConfiguration = { - type: "report", - id: "fakeReportIdInitialEmbed", - accessToken: 'fakeTokenInitialEmbed', - embedUrl: iframeSrc - }; - report = powerbi.embed($element[0], embedConfiguration); - page1 = report.page('ReportSection1'); - report2 = powerbi.embed($element2[0], embedConfiguration); - - iframe = $element.find('iframe')[0]; - iframe2 = $element2.find('iframe')[0]; - - /** - * Note: For testing we need to configure the eventSourceOverrideWindow to allow the host to respond to - * the iframe window; however, the iframe window doesn't exist until the first embed is created. - * - * To work around this we create a service for the initial embed, embed a report, then set the private variable - */ - (powerbi.wpmp).eventSourceOverrideWindow = iframe.contentWindow; - - // Register Iframe side - iframeHpm = setupEmbedMockApp(iframe.contentWindow, window, logMessages, 'SDK-to-MockApp IframeWpmp'); - iframeHpm2 = setupEmbedMockApp(iframe2.contentWindow, window, logMessages, 'SDK-to-MockApp IframeWpmp2'); - - // Reset load handler - spyApp.validateReportLoad.calls.reset(); - spyApp.validateDashboardLoad.calls.reset(); - spyApp.reset(); - - const iframe1Loaded = new Promise((resolve, reject) => { - iframe.addEventListener('load', () => { - resolve(null); - }); - }); - const iframe2Loaded = new Promise((resolve, reject) => { - iframe2.addEventListener('load', () => { - resolve(null); - }); - }); - - iframeLoaded = Promise.all([iframe1Loaded, iframe2Loaded]); - }); - - afterAll(function () { - powerbi.reset($element.get(0)); - $element.remove(); - powerbi.wpmp.stop(); - }); - - afterEach(function () { - spyApp.reset(); - }); - - describe('report', function () { - - beforeEach(function () { - spyOn(utils, "getTimeDiffInMilliseconds").and.callFake(() => 700); // Prevent requests from being throttled. - }); - - describe('load', function () { - it(`report.load() returns promise that rejects with validation errors if load configuration is invalid`, function (done) { - // Arrange - const testData = { - loadConfig: { - id: 'fakeReportId', - accessToken: 'fakeAccessToken' - }, - expectedErrors: [ - { - message: 'invalid load config' - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.validateReportLoad.and.returnValue(Promise.reject(testData.expectedErrors)); - // Act - let expectedConfiguration = utils.assign({}, report.config, testData.loadConfig); - report.config = expectedConfiguration; - report.load() - .catch(errors => { - // Assert - expect(spyApp.validateReportLoad).toHaveBeenCalledWith(expectedConfiguration); - expect(spyApp.reportLoad).not.toHaveBeenCalled(); - expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors)); - done(); - }); - }); - }); - - it('report.load() returns promise that resolves with null if the report load successful', function (done) { - // Arrange - const testData = { - loadConfig: { - id: 'fakeReportId', - accessToken: 'fakeAccessToken' - } - }; - - iframeLoaded - .then(() => { - spyApp.validateReportLoad.and.returnValue(Promise.resolve(null)); - spyApp.reportLoad.and.returnValue(Promise.resolve(null)); - // Act - let expectedConfiguration = utils.assign({}, report.config, testData.loadConfig); - report.config = expectedConfiguration; - report.load() - .then(response => { - // Assert - expect(spyApp.validateReportLoad).toHaveBeenCalledWith(expectedConfiguration); - expect(spyApp.reportLoad).toHaveBeenCalledWith(expectedConfiguration); - expect(response).toEqual(undefined); - done(); - }); - }); - }); - }); - - describe('pages', function () { - it('report.getPages() return promise that rejects with server error if there was error getting pages', function (done) { - // Arrange - const testData = { - expectedError: { - message: 'internal server error' - } - }; - - iframeLoaded - .then(() => { - spyApp.getPages.and.returnValue(Promise.reject(testData.expectedError)); - // Act - report.getPages() - .catch(error => { - // Assert - expect(spyApp.getPages).toHaveBeenCalled(); - expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); - done(); - }); - }); - }); - - it('report.getPages() returns promise that resolves with list of page names', function (done) { - // Arrange - const testData = { - pages: [ - { - name: "page1", - displayName: "Page 1", - isActive: true - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.getPages.and.returnValue(Promise.resolve(testData.pages)); - // Act - report.getPages() - .then(pages => { - // Assert - expect(spyApp.getPages).toHaveBeenCalled(); - // Workaround to compare pages - pages - .forEach(page => { - const testPage = util.find(p => p.name === page.name, testData.pages); - if (testPage) { - expect(page.name).toEqual(testPage.name); - expect(page.isActive).toEqual(testPage.isActive) - } - else { - expect(true).toBe(false); - } - }); - done(); - }); - }); - }); - }); - - describe('filters', function () { - it('report.getFilters() returns promise that rejects with server error if there was problem getting filters', function (done) { - // Arrange - const testData = { - expectedError: { - message: 'could not serialize filters' - } - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.reject(testData.expectedError)); - // Act - report.getFilters() - .catch(error => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); - done(); - }); - }); - }); - - it('report.getFilters() returns promise that resolves with filters is request is successful', function (done) { - // Arrange - const testData = { - filters: [ - { x: 'fakeFilter' } - ] - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); - // Act - report.getFilters() - .then(filters => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - // @ts-ignore as testData is not of type IFilter - expect(filters).toEqual(testData.filters); - done(); - }); - }); - }); - - it('report.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', function (done) { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() - ], - expectedErrors: [ - { - message: 'invalid filter' - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.reject(testData.expectedErrors)); - // Act - report.setFilters(testData.filters) - .catch(error => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).not.toHaveBeenCalled(); - expect(error).toEqual(jasmine.objectContaining(testData.expectedErrors)); - done(); - }); - }); - }); - - it('report.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', function (done) { - // Arrange - const testData = { - filters: [(new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON()] - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.resolve(null)); - spyApp.setFilters.and.returnValue(Promise.resolve(null)); - // Act - report.setFilters(testData.filters) - .then(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); - done(); - }); - }); - }); - - it('report.removeFilters() returns promise that resolves with null if the request was accepted', function (done) { - // Arrange - iframeLoaded - .then(() => { - spyApp.updateFilters.and.returnValue(Promise.resolve(null)); - // Act - report.removeFilters() - .then(response => { - // Assert - expect(spyApp.updateFilters).toHaveBeenCalledWith(models.FiltersOperations.RemoveAll, undefined); - done(); - }); - }); - }); - }); - - describe('print', function () { - it('report.print() returns promise that resolves with null if the report print command was accepted', function (done) { - // Arrange - iframeLoaded - .then(() => { - spyApp.print.and.returnValue(Promise.resolve(null)); - // Act - report.print() - .then(response => { - // Assert - expect(spyApp.print).toHaveBeenCalled(); - expect(response).toEqual(undefined); - done(); - }); - }); - }); - }); - - describe('refresh', function () { - it('report.refresh() returns promise that resolves with null if the report refresh command was accepted', function (done) { - // Arrange - iframeLoaded - .then(() => { - spyApp.refreshData.and.returnValue(Promise.resolve(null)); - // Act - report.refresh() - .then(response => { - // Assert - expect(spyApp.refreshData).toHaveBeenCalled(); - expect(response).toEqual(undefined); - done(); - }); - }); - }); - }); - - describe('settings', function () { - it('report.updateSettings(setting) returns promise that rejects with validation error if object is invalid', function (done) { - // Arrange - const testData = { - settings: { - filterPaneEnabled: false - }, - expectedErrors: [ - { - message: 'invalid target' - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.validateSettings.and.returnValue(Promise.reject(testData.expectedErrors)); - // Act - report.updateSettings(testData.settings) - .catch(errors => { - // Assert - expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); - expect(spyApp.updateSettings).not.toHaveBeenCalled(); - expect(errors).toEqual(jasmine.objectContaining(testData.expectedErrors)); - done(); - }); - }); - }); - - it('report.updateSettings(settings) returns promise that resolves with null if requst is valid and accepted', function (done) { - // Arrange - const testData = { - settings: { - filterPaneEnabled: false - }, - expectedErrors: [ - { - message: 'invalid target' - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.validateSettings.and.returnValue(Promise.resolve(null)); - spyApp.updateSettings.and.returnValue(Promise.resolve(null)); - // Act - report.updateSettings(testData.settings) - .then(response => { - // Assert - expect(spyApp.validateSettings).toHaveBeenCalledWith(testData.settings); - expect(spyApp.updateSettings).toHaveBeenCalledWith(testData.settings); - done(); - }); - }); - }); - }); - }); - - describe('page', function () { - describe('filters', function () { - it('page.getFilters() returns promise that rejects with server error if there was problem getting filters', function (done) { - // Arrange - const testData = { - expectedError: { - message: 'could not serialize filters' - } - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.reject(testData.expectedError)); - // Act - page1.getFilters() - .catch(error => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - expect(error).toEqual(jasmine.objectContaining(testData.expectedError)); - done(); - }); - }); - }); - - it('page.getFilters() returns promise that resolves with filters is request is successful', function (done) { - // Arrange - const testData = { - filters: [ - { x: 'fakeFilter' } - ] - }; - - iframeLoaded - .then(() => { - spyApp.getFilters.and.returnValue(Promise.resolve(testData.filters)); - // Act - page1.getFilters() - .then(filters => { - // Assert - expect(spyApp.getFilters).toHaveBeenCalled(); - // @ts-ignore as testData is not of type IFilter as testData is not of type IFilter - expect(filters).toEqual(testData.filters); - done(); - }); - }); - }); - - it('page.setFilters(filters) returns promise that rejects with validation errors if filter is invalid', function (done) { - // Arrange - const testData = { - filters: [ - (new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON() - ], - expectedErrors: [ - { - message: 'invalid filter' - } - ] - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.reject(testData.expectedErrors)); - // Act - page1.setFilters(testData.filters) - .catch(error => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).not.toHaveBeenCalled(); - expect(error).toEqual(jasmine.objectContaining(testData.expectedErrors)); - done(); - }); - }); - }); - - it('page.setFilters(filters) returns promise that resolves with null if filter was valid and request is accepted', function (done) { - // Arrange - const testData = { - filters: [(new models.BasicFilter({ table: "cars", column: "make" }, "In", ["subaru", "honda"])).toJSON()] - }; - - iframeLoaded - .then(() => { - spyApp.validateFilter.and.returnValue(Promise.resolve(null)); - spyApp.setFilters.and.returnValue(Promise.resolve(null)); - // Act - page1.setFilters(testData.filters) - .then(response => { - // Assert - expect(spyApp.validateFilter).toHaveBeenCalledWith(testData.filters[0]); - expect(spyApp.setFilters).toHaveBeenCalledWith(testData.filters); - done(); - }); - }); - }); - - it('page.removeFilters() returns promise that resolves with null if the request was accepted', function (done) { - // Arrange - iframeLoaded - .then(() => { - spyApp.updateFilters.and.returnValue(Promise.resolve(null)); - // Act - page1.removeFilters() - .then(response => { - // Assert - expect(spyApp.updateFilters).toHaveBeenCalledWith(models.FiltersOperations.RemoveAll, undefined); - done(); - }); - }); - }); - }); - - describe('setActive', function () { - it('page.setActive() returns promise that rejects if page is invalid', function (done) { - // Arrange - const testData = { - errors: [ - { - message: 'page xyz was not found in report' - } - ] - }; - - // Act - iframeLoaded - .then(() => { - spyApp.validatePage.and.returnValue(Promise.reject(testData.errors)); - - // Act - page1.setActive() - .catch(errors => { - // Assert - expect(spyApp.validatePage).toHaveBeenCalled(); //.toHaveBeenCalledWith(page1); - expect(spyApp.setPage).not.toHaveBeenCalled(); - expect(errors).toEqual(jasmine.objectContaining(testData.errors)); - done(); - }); - }); - }); - - it('page.setActive() returns promise that resolves with null if request is successful', function (done) { - // Arrange - const testData = { - errors: [ - { - message: 'page xyz was not found in report' - } - ] - }; - - // Act - iframeLoaded - .then(() => { - setTimeout(() => { - spyApp.validatePage.and.returnValue(Promise.resolve(null)); - spyApp.setPage.and.returnValue(Promise.resolve(null)); - // Act - page1.setActive() - .then(() => { - // Assert - expect(spyApp.validatePage).toHaveBeenCalled(); //.toHaveBeenCalledWith(page1); - expect(spyApp.setPage).toHaveBeenCalled(); //.toHaveBeenCalledWith(page1); - done(); - }); - }, 500); - }); - }); - }); - }); - - describe('SDK-to-Router (Event subscription)', function () { - it(`report.on(eventName, handler) should throw error if eventName is not supported`, function () { - // Arrange - const testData = { - eventName: 'xyz', - handler: jasmine.createSpy('handler') - }; - - // Act - const attemptToSubscribeToEvent = () => { - report.on(testData.eventName, testData.handler); - }; - - // Assert - expect(attemptToSubscribeToEvent).toThrowError(); - }); - - it(`report.on(eventName, handler) should register handler and be called when POST /report/:uniqueId/events/:eventName is received`, function (done) { - // Arrange - const testData = { - reportId: 'fakeReportId', - eventName: 'pageChanged', - handler: jasmine.createSpy('handler'), - simulatedPageChangeBody: { - initiator: 'sdk', - newPage: { - name: 'page1', - displayName: 'Page 1' - } - }, - expectedEvent: { - detail: { - initiator: 'sdk', - newPage: report.page('page1') - } - } - }; - const testDataHandler: jasmine.Spy = testData.handler; - - report.on(testData.eventName, testData.handler); - - // Act - iframeHpm.post(`/reports/${report.config.uniqueId}/events/${testData.eventName}`, testData.simulatedPageChangeBody) - .then(response => { - // Assert - expect(testData.handler).toHaveBeenCalledWith(jasmine.any(CustomEvent)); - // Workaround to compare pages which prevents recursive loop in jasmine equals - // expect(testData.handler2).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedPageChangeBody })); - expect(testData.handler.calls.mostRecent().args[0].detail.newPage.name).toEqual(testData.expectedEvent.detail.newPage.name); - done(); - }); - }); - - it(`if multiple reports with the same id are loaded into the host, and event occurs on one of them, only one report handler should be called`, function (done) { - // Arrange - const testData = { - reportId: 'fakeReportId', - eventName: 'pageChanged', - handler: jasmine.createSpy('handler'), - handler2: jasmine.createSpy('handler2'), - simulatedPageChangeBody: { - initiator: 'sdk', - newPage: { - name: 'page1', - displayName: 'Page 1' - } - } - }; - - report.on(testData.eventName, testData.handler); - report2.on(testData.eventName, testData.handler2); - - // Act - iframeHpm.post(`/reports/${report2.config.uniqueId}/events/${testData.eventName}`, testData.simulatedPageChangeBody) - .then(response => { - expect(testData.handler).not.toHaveBeenCalled(); - expect(testData.handler2).toHaveBeenCalledWith(jasmine.any(CustomEvent)); - // Workaround to compare pages which prevents recursive loop in jasmine equals - // expect(testData.handler).toHaveBeenCalledWith(jasmine.objectContaining(testData.expectedEvent)); - expect(testData.handler2.calls.mostRecent().args[0].detail.newPage.name).toEqual(testData.simulatedPageChangeBody.newPage.name); - done(); - }); - }); - - it(`ensure load event is allowed`, function (done) { - // Arrange - const testData = { - reportId: 'fakeReportId', - eventName: 'loaded', - handler: jasmine.createSpy('handler3'), - simulatedBody: { - initiator: 'sdk' - } - }; - - report.on(testData.eventName, testData.handler); - - // Act - iframeHpm.post(`/reports/${report.config.uniqueId}/events/${testData.eventName}`, testData.simulatedBody) - .then(response => { - // Assert - expect(testData.handler).toHaveBeenCalledWith(jasmine.any(CustomEvent)); - expect(testData.handler).toHaveBeenCalledWith(jasmine.objectContaining({ detail: testData.simulatedBody })); - done(); - }); - }); - }); -}); \ No newline at end of file diff --git a/test/utility/mockApp.ts b/test/utility/mockApp.ts index e91a634a..7ef1aab3 100644 --- a/test/utility/mockApp.ts +++ b/test/utility/mockApp.ts @@ -90,42 +90,77 @@ export const mockAppSpyObj = { setAccessToken: jasmine.createSpy("setAccessToken").and.returnValue(Promise.resolve(null)), switchLayout: jasmine.createSpy("switchLayout").and.returnValue(Promise.resolve(null)), - reset() { + reset(): void { mockAppSpyObj.dashboardLoad.calls.reset(); + mockAppSpyObj.dashboardLoad.and.callThrough(); mockAppSpyObj.validateDashboardLoad.calls.reset(); + mockAppSpyObj.validateDashboardLoad.and.callThrough(); mockAppSpyObj.reportLoad.calls.reset(); + mockAppSpyObj.reportLoad.and.callThrough(); mockAppSpyObj.render.calls.reset(); + mockAppSpyObj.render.and.callThrough(); mockAppSpyObj.validateReportLoad.calls.reset(); + mockAppSpyObj.validateReportLoad.and.callThrough(); mockAppSpyObj.updateSettings.calls.reset(); + mockAppSpyObj.updateSettings.and.callThrough(); mockAppSpyObj.validateSettings.calls.reset(); + mockAppSpyObj.validateSettings.and.callThrough(); mockAppSpyObj.setVisualDisplayState.calls.reset(); + mockAppSpyObj.setVisualDisplayState.and.callThrough(); mockAppSpyObj.resizeVisual.calls.reset(); + mockAppSpyObj.resizeVisual.and.callThrough(); mockAppSpyObj.resizeActivePage.calls.reset(); + mockAppSpyObj.resizeActivePage.and.callThrough(); mockAppSpyObj.moveVisual.calls.reset(); + mockAppSpyObj.moveVisual.and.callThrough(); mockAppSpyObj.getPages.calls.reset(); + mockAppSpyObj.getPages.and.callThrough(); mockAppSpyObj.getPageByName.calls.reset(); + mockAppSpyObj.getPageByName.and.callThrough(); mockAppSpyObj.getActivePage.calls.reset(); + mockAppSpyObj.getActivePage.and.callThrough(); mockAppSpyObj.setPage.calls.reset(); + mockAppSpyObj.setPage.and.callThrough(); mockAppSpyObj.validatePage.calls.reset(); + mockAppSpyObj.validatePage.and.callThrough(); mockAppSpyObj.validateVisual.calls.reset(); + mockAppSpyObj.validateVisual.and.callThrough(); mockAppSpyObj.getVisualByName.calls.reset(); + mockAppSpyObj.getVisualByName.and.callThrough(); mockAppSpyObj.getFilters.calls.reset(); + mockAppSpyObj.getFilters.and.callThrough(); mockAppSpyObj.updateFilters.calls.reset(); + mockAppSpyObj.updateFilters.and.callThrough(); mockAppSpyObj.setFilters.calls.reset(); + mockAppSpyObj.setFilters.and.callThrough(); mockAppSpyObj.validateFilter.calls.reset(); + mockAppSpyObj.validateFilter.and.callThrough(); mockAppSpyObj.addContextMenuCommand.calls.reset(); + mockAppSpyObj.addContextMenuCommand.and.callThrough(); mockAppSpyObj.addOptionsMenuCommand.calls.reset(); + mockAppSpyObj.addOptionsMenuCommand.and.callThrough(); mockAppSpyObj.removeContextMenuCommand.calls.reset(); + mockAppSpyObj.removeContextMenuCommand.and.callThrough(); mockAppSpyObj.removeOptionsMenuCommand.calls.reset(); + mockAppSpyObj.removeOptionsMenuCommand.and.callThrough(); mockAppSpyObj.print.calls.reset(); + mockAppSpyObj.print.and.callThrough(); mockAppSpyObj.refreshData.calls.reset(); + mockAppSpyObj.refreshData.and.callThrough(); mockAppSpyObj.exportData.calls.reset(); + mockAppSpyObj.exportData.and.callThrough(); mockAppSpyObj.validateCreateReport.calls.reset(); + mockAppSpyObj.validateCreateReport.and.callThrough(); mockAppSpyObj.switchMode.calls.reset(); + mockAppSpyObj.switchMode.and.callThrough(); mockAppSpyObj.save.calls.reset(); + mockAppSpyObj.save.and.callThrough(); mockAppSpyObj.saveAs.calls.reset(); + mockAppSpyObj.saveAs.and.callThrough(); mockAppSpyObj.setAccessToken.calls.reset(); + mockAppSpyObj.setAccessToken.and.callThrough(); mockAppSpyObj.switchLayout.calls.reset(); + mockAppSpyObj.switchLayout.and.callThrough(); } }; diff --git a/test/utility/mockEmbed.ts b/test/utility/mockEmbed.ts index 0cbf3b46..737a6e62 100644 --- a/test/utility/mockEmbed.ts +++ b/test/utility/mockEmbed.ts @@ -1,293 +1,271 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import * as Wpmp from 'window-post-message-proxy'; -import * as Hpm from 'http-post-message'; -import * as Router from 'powerbi-router'; -import * as models from 'powerbi-models'; +import { WindowPostMessageProxy } from 'window-post-message-proxy'; +import { HttpPostMessage } from 'http-post-message'; +import { Router } from 'powerbi-router'; import { mockAppSpyObj, mockApp } from './mockApp'; +import * as models from 'powerbi-models'; export const spyApp = mockAppSpyObj; -export function setupEmbedMockApp(iframeContentWindow: Window, parentWindow: Window, logMessages: boolean, name: string = 'MockAppWindowPostMessageProxy'): Hpm.HttpPostMessage { +export function setupEmbedMockApp(iframeContentWindow: Window, parentWindow: Window, logMessages: boolean, name: string = 'MockAppWindowPostMessageProxy'): HttpPostMessage { const parent = parentWindow || iframeContentWindow.parent; - const wpmp = new Wpmp.WindowPostMessageProxy({ + const wpmp = new WindowPostMessageProxy({ processTrackingProperties: { - addTrackingProperties: Hpm.HttpPostMessage.addTrackingProperties, - getTrackingProperties: Hpm.HttpPostMessage.getTrackingProperties, + addTrackingProperties: HttpPostMessage.addTrackingProperties, + getTrackingProperties: HttpPostMessage.getTrackingProperties, }, - isErrorMessage: Hpm.HttpPostMessage.isErrorMessage, + isErrorMessage: HttpPostMessage.isErrorMessage, receiveWindow: iframeContentWindow, name, logMessages }); - const hpm = new Hpm.HttpPostMessage(wpmp, { + const hpm = new HttpPostMessage(wpmp, { 'origin': 'reportEmbedMock', 'x-version': '1.0.0' }, parent); - const router = new Router.Router(wpmp); + const router = new Router(wpmp); const app = mockApp; /** * Setup not found handlers. */ - function notFoundHandler(req, res) { + function notFoundHandler(req, res): void { res.send(404, `Not Found. Url: ${req.params.notfound} was not found.`); - }; + } router.get('*notfound', notFoundHandler); router.post('*notfound', notFoundHandler); router.patch('*notfound', notFoundHandler); router.put('*notfound', notFoundHandler); router.delete('*notfound', notFoundHandler); - /** - * Phase 1 - */ - /** * Dashboard Embed */ - router.post('/dashboard/load', (req, res) => { + router.post('/dashboard/load', async (req, res) => { const uniqueId = req.headers['uid']; const loadConfig = req.body; - return app.validateDashboardLoad(loadConfig) - .then(() => { - app.dashboardLoad(loadConfig) - .then(() => { - const initiator = "sdk"; - hpm.post(`/dashboards/${uniqueId}/events/loaded`, { - initiator - }); - }, error => { - hpm.post(`/dashboards/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, error => { - res.send(400, error); - }); + try { + await app.validateDashboardLoad(loadConfig); + try { + await app.dashboardLoad(loadConfig); + hpm.post(`/dashboards/${uniqueId}/events/loaded`, { + initiator: "sdk" + }); + } catch (error) { + hpm.post(`/dashboards/${uniqueId}/events/error`, error); + } + res.send(202, {}); + } catch (error) { + res.send(400, error); + } }); /** * Create Report */ - router.post('/report/create', (req, res) => { + router.post('/report/create', async (req, res) => { const uniqueId = req.headers['uid']; const createConfig = req.body; - return app.validateCreateReport(createConfig) - .then(() => { - app.reportLoad(createConfig) - .then(() => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/events/loaded`, { - initiator - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, error => { - res.send(400, error); - }); + try { + await app.validateCreateReport(createConfig); + try { + await app.reportLoad(createConfig); + hpm.post(`/reports/${uniqueId}/events/loaded`, { + initiator: "sdk" + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + } catch (error) { + res.send(400, error); + } }); /** * Report Embed */ - router.post('/report/load', (req, res) => { + router.post('/report/load', async (req, res) => { const uniqueId = req.headers['uid']; const loadConfig = req.body; - return app.validateReportLoad(loadConfig) - .then(() => { - app.reportLoad(loadConfig) - .then(() => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/events/loaded`, { - initiator - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, error => { - res.send(400, error); - }); + try { + await app.validateReportLoad(loadConfig); + try { + await app.reportLoad(loadConfig); + hpm.post(`/reports/${uniqueId}/events/loaded`, { + initiator: "sdk" + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + + } catch (error) { + res.send(400, error); + } }); /** * Report Embed */ - router.post('/report/prepare', (req, res) => { + router.post('/report/prepare', async (req, res) => { const uniqueId = req.headers['uid']; const loadConfig = req.body; - return app.validateReportLoad(loadConfig) - .then(() => { - app.reportLoad(loadConfig) - .then(() => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/events/loaded`, { - initiator - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, error => { - res.send(400, error); - }); + try { + await app.validateReportLoad(loadConfig); + try { + await app.reportLoad(loadConfig); + hpm.post(`/reports/${uniqueId}/events/loaded`, { + initiator: "sdk" + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + + } catch (error) { + res.send(400, error); + } }); router.post('/report/render', (req, res) => { app.render(); - res.send(202); + res.send(202, {}); }); - router.get('/report/pages', (req, res) => { - return app.getPages() - .then(pages => { - res.send(200, pages); - }, error => { - res.send(500, error); - }); + router.get('/report/pages', async (req, res) => { + try { + const pages = await app.getPages(); + res.send(200, pages); + + } catch (error) { + res.send(500, error); + } }); - router.put('/report/pages/active', (req, res) => { + router.put('/report/pages/active', async (req, res) => { const uniqueId = req.headers['uid']; const page = req.body; - return app.validatePage(page) - .then(() => { - app.setPage(page) - .then(() => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/events/pageChanged`, { - initiator, - newPage: page - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, errors => { - res.send(400, errors); - }); + try { + await app.validatePage(page); + try { + await app.setPage(page); + hpm.post(`/reports/${uniqueId}/events/pageChanged`, { + initiator: "sdk", + newPage: page + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202); + } catch (error) { + res.send(400, error); + } }); - /** - * Phase 2 - */ - router.get('/report/filters', (req, res) => { - return app.getFilters() - .then(filters => { - res.send(200, filters); - }, error => { - res.send(500, error); - }); + router.get('/report/filters', async (req, res) => { + try { + const filters = await app.getFilters(); + res.send(200, filters); + } catch (error) { + res.send(500, error); + } }); - router.put('/report/filters', (req, res) => { + router.put('/report/filters', async (req, res) => { const uniqueId = req.headers['uid']; const filters = req.body; - return Promise.all(filters.map(filter => app.validateFilter(filter))) - .then(() => { - app.setFilters(filters) - .then(filter => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/events/filtersApplied`, { - initiator, - filter - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, error => { - res.send(400, error); - }); + try { + await Promise.all(filters.map(filter => app.validateFilter(filter))); + try { + const filter = await app.setFilters(filters); + hpm.post(`/reports/${uniqueId}/events/filtersApplied`, { + initiator: "sdk", + filter + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + } catch (error) { + res.send(400, error); + } }); - router.post('/report/filters', (req, res) => { + router.post('/report/filters', async (req, res) => { const uniqueId = req.headers['uid']; - const operation = req.body.filtersOperation + const operation = req.body.filtersOperation; const filters = req.body.filters; - return Promise.all(filters ? filters.map(filter => app.validateFilter(filter)) : [Promise.resolve(null)]) - .then(() => { - app.updateFilters(operation, filters) - .then(filter => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/events/filtersApplied`, { - initiator, - filter - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, error => { - res.send(400, error); - }); + try { + Promise.all(filters ? filters.map(filter => app.validateFilter(filter)) : [Promise.resolve(null)]); + try { + const filter = await app.updateFilters(operation, filters); + hpm.post(`/reports/${uniqueId}/events/filtersApplied`, { + initiator: "sdk", + filter + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + } catch (error) { + res.send(400, error); + } }); - /** - * Phase 3 - */ - router.get('/report/pages/:pageName/filters', (req, res) => { + router.get('/report/pages/:pageName/filters', async (req, res) => { const page = { name: req.params.pageName, displayName: null }; - - return app.validatePage(page) - .then(() => { - return app.getFilters() - .then(filters => { - res.send(200, filters); - }, error => { - res.send(500, error); - }); - }, errors => { - res.send(400, errors); - }); + try { + await app.validatePage(page); + try { + const filters = await app.getFilters(); + res.send(200, filters); + } catch (error) { + res.send(500, error); + } + } catch (error) { + res.send(400, error); + } }); - router.post('/report/pages/:pageName/filters', (req, res) => { + router.post('/report/pages/:pageName/filters', async (req, res) => { const pageName = req.params.pageName; const uniqueId = req.headers['uid']; - const operation = req.body.filtersOperation + const operation = req.body.filtersOperation; const filters = req.body.filters; const page: models.IPage = { name: pageName, displayName: null }; - return app.validatePage(page) - .then(() => Promise.all(filters ? filters.map(filter => app.validateFilter(filter)) : [Promise.resolve(null)])) - .then(() => { - app.updateFilters(operation, filters) - .then(filter => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/pages/${pageName}/events/filtersApplied`, { - initiator, - filter - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, errors => { - res.send(400, errors); - }); + try { + await app.validatePage(page); + await Promise.all(filters ? filters.map(filter => app.validateFilter(filter)) : [Promise.resolve(null)]); + try { + const filter = await app.updateFilters(operation, filters); + hpm.post(`/reports/${uniqueId}/pages/${pageName}/events/filtersApplied`, { + initiator: "sdk", + filter + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + + } catch (error) { + res.send(400, error); + + } }); - router.put('/report/pages/:pageName/filters', (req, res) => { + router.put('/report/pages/:pageName/filters', async (req, res) => { const pageName = req.params.pageName; const uniqueId = req.headers['uid']; const filters = req.body; @@ -295,28 +273,25 @@ export function setupEmbedMockApp(iframeContentWindow: Window, parentWindow: Win name: pageName, displayName: null }; - - return app.validatePage(page) - .then(() => Promise.all(filters.map(filter => app.validateFilter(filter)))) - .then(() => { - app.setFilters(filters) - .then(filter => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/pages/${pageName}/events/filtersApplied`, { - initiator, - filter - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, errors => { - res.send(400, errors); - }); + try { + await app.validatePage(page); + await Promise.all(filters.map(filter => app.validateFilter(filter))); + try { + const filter = await app.setFilters(filters); + hpm.post(`/reports/${uniqueId}/pages/${pageName}/events/filtersApplied`, { + initiator: "sdk", + filter + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + } catch (error) { + res.send(400, error); + } }); - router.get('/report/pages/:pageName/visuals/:visualName/filters', (req, res) => { + router.get('/report/pages/:pageName/visuals/:visualName/filters', async (req, res) => { const page = { name: req.params.pageName, displayName: null @@ -328,24 +303,24 @@ export function setupEmbedMockApp(iframeContentWindow: Window, parentWindow: Win layout: {}, }; - return app.validateVisual(page, visual) - .then(() => { - return app.getFilters() - .then(filters => { - res.send(200, filters); - }, error => { - res.send(500, error); - }); - }, errors => { - res.send(400, errors); - }); + try { + await app.validateVisual(page, visual); + try { + const filters = await app.getFilters(); + res.send(200, filters); + } catch (error) { + res.send(500, error); + } + } catch (error) { + res.send(400, error); + } }); - router.post('/report/pages/:pageName/visuals/:visualName/filters', (req, res) => { + router.post('/report/pages/:pageName/visuals/:visualName/filters', async (req, res) => { const pageName = req.params.pageName; const visualName = req.params.visualName; const uniqueId = req.headers['uid']; - const operation = req.body.filtersOperation + const operation = req.body.filtersOperation; const filters = req.body.filters; const page: models.IPage = { name: pageName, displayName: null @@ -357,27 +332,25 @@ export function setupEmbedMockApp(iframeContentWindow: Window, parentWindow: Win layout: {}, }; - return app.validateVisual(page, visual) - .then(() => Promise.all(filters ? filters.map(filter => app.validateFilter(filter)) : [Promise.resolve(null)])) - .then(() => { - app.updateFilters(operation, filters) - .then(filter => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/pages/${pageName}/visuals/${visualName}/events/filtersApplied`, { - initiator, - filter - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, errors => { - res.send(400, errors); - }); + try { + await app.validateVisual(page, visual); + await Promise.all(filters ? filters.map(filter => app.validateFilter(filter)) : [Promise.resolve(null)]); + try { + const filter = await app.updateFilters(operation, filters); + hpm.post(`/reports/${uniqueId}/pages/${pageName}/visuals/${visualName}/events/filtersApplied`, { + initiator: "sdk", + filter + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + } catch (error) { + res.send(400, error); + } }); - router.put('/report/pages/:pageName/visuals/:visualName/filters', (req, res) => { + router.put('/report/pages/:pageName/visuals/:visualName/filters', async (req, res) => { const pageName = req.params.pageName; const visualName = req.params.visualName; const uniqueId = req.headers['uid']; @@ -392,58 +365,48 @@ export function setupEmbedMockApp(iframeContentWindow: Window, parentWindow: Win type: 'type', layout: {}, }; - - return app.validateVisual(page, visual) - .then(() => Promise.all(filters.map(filter => app.validateFilter(filter)))) - .then(() => { - app.setFilters(filters) - .then(filter => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/pages/${pageName}/visuals/${visualName}/events/filtersApplied`, { - initiator, - filter - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, errors => { - res.send(400, errors); - }); + try { + await app.validateVisual(page, visual); + await Promise.all(filters.map(filter => app.validateFilter(filter))); + try { + const filter = await app.setFilters(filters); + hpm.post(`/reports/${uniqueId}/pages/${pageName}/visuals/${visualName}/events/filtersApplied`, { + initiator: "sdk", + filter + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + } catch (error) { + res.send(400, error); + } }); - router.patch('/report/settings', (req, res) => { + router.patch('/report/settings', async (req, res) => { const uniqueId = req.headers['uid']; const settings = req.body; - - return app.validateSettings(settings) - .then(() => { - app.updateSettings(settings) - .then(updatedSettings => { - const initiator = "sdk"; - hpm.post(`/reports/${uniqueId}/events/settingsUpdated`, { - initiator, - settings: updatedSettings - }); - }, error => { - hpm.post(`/reports/${uniqueId}/events/error`, error); - }); - - res.send(202); - }, errors => { - res.send(400, errors); - }); + try { + await app.validateSettings(settings); + try { + const updatedSettings = await app.updateSettings(settings); + hpm.post(`/reports/${uniqueId}/events/settingsUpdated`, { + initiator: "sdk", + settings: updatedSettings + }); + } catch (error) { + hpm.post(`/reports/${uniqueId}/events/error`, error); + } + res.send(202, {}); + } + catch (error) { + res.send(400, error); + } }); - /** - * Phase 4 - */ - router.get('/report/data', (req, res) => { - return app.exportData() - .then(data => { - res.send(200, data); - }); + router.get('/report/data', async (req, res) => { + const data = await app.exportData(); + res.send(200, data); }); router.post('/report/refresh', (req, res) => { @@ -477,6 +440,5 @@ export function setupEmbedMockApp(iframeContentWindow: Window, parentWindow: Win app.setAccessToken(settings); res.send(202); }); - return hpm; -} \ No newline at end of file +} diff --git a/test/utility/mockHpm.ts b/test/utility/mockHpm.ts index 3287af81..2bc5ca50 100644 --- a/test/utility/mockHpm.ts +++ b/test/utility/mockHpm.ts @@ -2,9 +2,9 @@ // Licensed under the MIT License. export const spyHpm = { - get: jasmine.createSpy("get").and.returnValue(Promise.resolve(null)), - post: jasmine.createSpy("post").and.returnValue(Promise.resolve(null)), - patch: jasmine.createSpy("patch").and.returnValue(Promise.resolve(null)), - put: jasmine.createSpy("put").and.returnValue(Promise.resolve(null)), - delete: jasmine.createSpy("delete").and.returnValue(Promise.resolve(null)) -}; \ No newline at end of file + get: jasmine.createSpy("get").and.returnValue(Promise.resolve({})), + post: jasmine.createSpy("post").and.returnValue(Promise.resolve({})), + patch: jasmine.createSpy("patch").and.returnValue(Promise.resolve({})), + put: jasmine.createSpy("put").and.returnValue(Promise.resolve({})), + delete: jasmine.createSpy("delete").and.returnValue(Promise.resolve({})) +}; diff --git a/test/utility/mockRouter.ts b/test/utility/mockRouter.ts index 2223bf40..8a3e692a 100644 --- a/test/utility/mockRouter.ts +++ b/test/utility/mockRouter.ts @@ -7,4 +7,4 @@ export const spyRouter = { patch: jasmine.createSpy("patch"), put: jasmine.createSpy("put"), delete: jasmine.createSpy("delete") -}; \ No newline at end of file +}; diff --git a/test/utility/mockWpmp.ts b/test/utility/mockWpmp.ts index 2603ac55..dd19c85b 100644 --- a/test/utility/mockWpmp.ts +++ b/test/utility/mockWpmp.ts @@ -4,18 +4,18 @@ export const spyWpmp = { handlers: [], - clearHandlers() { + clearHandlers(): void { spyWpmp.handlers.length = 0; }, - addHandlerSpy(handler) { + addHandlerSpy(handler: any): void { spyWpmp.handlers.push(handler); }, addHandler: jasmine.createSpy("addHandler").and.callFake((x) => spyWpmp.addHandlerSpy(x)), postMessageSpy: jasmine.createSpy("postMessage"), - postMessage(message): Promise { + postMessage(message: any): Promise { spyWpmp.postMessageSpy(message); return Promise.resolve(null); }, @@ -23,12 +23,12 @@ export const spyWpmp = { start: jasmine.createSpy("start"), stop: jasmine.createSpy("stop"), - onMessageReceived(event) { + onMessageReceived(event: any): void { let message: any = event.data; const handled = spyWpmp.handlers.some(handler => { if (handler.test(message)) { - Promise.resolve(handler.handle(message)) + Promise.resolve(handler.handle(message)); return true; } @@ -38,4 +38,4 @@ export const spyWpmp = { throw Error(`nothing handled message`); } } -}; \ No newline at end of file +}; diff --git a/webpack.test.config.js b/webpack.test.config.js index 04c1f1ff..b3ac3c48 100644 --- a/webpack.test.config.js +++ b/webpack.test.config.js @@ -3,7 +3,7 @@ const glob = require("glob"); module.exports = { mode: 'development', - entry: glob.sync('./test/*.spec.ts'), + entry: glob.sync('./test/**/*.spec.ts'), output: { path: __dirname + "/tmp", filename: 'test.spec.js'