Tutorial: Hinzufügen von Komponententests zu Power BI-VisualprojektenTutorial: Add unit tests for Power BI visual projects

In diesem Artikel werden die Grundlagen zur Erstellung von Komponententests für Power BI-Visualprojekte beschrieben. Dabei wird u. a. Folgendes thematisiert:This article describes the basics of writing unit tests for your Power BI visuals, including how to:

  • Einrichten des Testframeworks Jasmine und des Test Runners Karma für JavaScript-CodeSet up the Karma JavaScript test runner testing framework, Jasmine.
  • Verwenden des Pakets „powerbi-visuals-utils-testutils“Use the powerbi-visuals-utils-testutils package.
  • Verwenden von Pseudo- und Fakeobjekten, um Komponententests für Power BI-Visuals zu vereinfachenUse mocks and fakes to help simplify unit testing of Power BI visuals.

VoraussetzungenPrerequisites

  • Ein installiertes Power BI-VisualprojektAn installed Power BI visuals project
  • Eine konfigurierte Node.js-UmgebungA configured Node.js environment

Installieren und Konfigurieren des Jasmine-Testframeworks und des Test Runners Karma für JavaScript-CodeInstall and configure the Karma JavaScript test runner and Jasmine

Fügen Sie der Datei package.json im Abschnitt devDependencies die erforderlichen Bibliotheken hinzu:Add the required libraries to the package.json file in the devDependencies section:

"@babel/polyfill": "^7.2.5",
"@types/d3": "5.5.0",
"@types/jasmine": "2.5.37",
"@types/jasmine-jquery": "1.5.28",
"@types/jquery": "2.0.41",
"@types/karma": "3.0.0",
"@types/lodash-es": "4.17.1",
"coveralls": "3.0.2",
"istanbul-instrumenter-loader": "^3.0.1",
"jasmine": "2.5.2",
"jasmine-core": "2.5.2",
"jasmine-jquery": "2.1.1",
"jquery": "3.1.1",
"karma": "3.1.1",
"karma-chrome-launcher": "2.2.0",
"karma-coverage": "1.1.2",
"karma-coverage-istanbul-reporter": "^2.0.4",
"karma-jasmine": "2.0.1",
"karma-junit-reporter": "^1.2.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-typescript": "^3.0.13",
"karma-typescript-preprocessor": "0.4.0",
"karma-webpack": "3.0.5",
"puppeteer": "1.17.0",
"style-loader": "0.23.1",
"ts-loader": "5.3.0",
"ts-node": "7.0.1",
"tslint": "^5.12.0",
"webpack": "4.26.0"

Weitere Informationen zu package.json finden Sie in der Beschreibung unter npm-package.json.To learn more about package.json, see the description at npm-package.json.

Speichern Sie die Datei package.json. Führen Sie anschließend am Speicherort von package.json den folgenden Befehl aus:Save the package.json file and, at the package.json location, run the following command:

npm install

Der Paket-Manager installiert alle neuen Pakete, die package.json hinzugefügt wurden.The package manager installs all new packages that are added to package.json.

Konfigurieren Sie den Test Runner und die webpack-Konfiguration, um Komponententests auszuführen.To run unit tests, configure the test runner and webpack config.

Der folgende Code ist ein Beispiel aus der Datei test.webpack.config.js:The following code is a sample of the test.webpack.config.js file:

const path = require('path');
const webpack = require("webpack");

module.exports = {
    devtool: 'source-map',
    mode: 'development',
    optimization : {
        concatenateModules: false,
        minimize: false
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                use: 'ts-loader',
                exclude: /node_modules/
            },
            {
                test: /\.json$/,
                loader: 'json-loader'
            },
            {
                test: /\.tsx?$/i,
                enforce: 'post',
                include: /(src)/,
                exclude: /(node_modules|resources\/js\/vendor)/,
                loader: 'istanbul-instrumenter-loader',
                options: { esModules: true }
            },
            {
                test: /\.less$/,
                use: [
                    {
                        loader: 'style-loader'
                    },
                    {
                        loader: 'css-loader'
                    },
                    {
                        loader: 'less-loader',
                        options: {
                            paths: [path.resolve(__dirname, 'node_modules')]
                        }
                    }
                ]
            }
        ]
    },
    externals: {
        "powerbi-visuals-api": '{}'
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js', '.css']
    },
    output: {
        path: path.resolve(__dirname, ".tmp/test")
    },
    plugins: [
        new webpack.ProvidePlugin({
            'powerbi-visuals-api': null
        })
    ]
};

Der folgende Code ist ein Beispiel aus der Datei karma.conf.ts:The following code is a sample of the karma.conf.ts file:

"use strict";

const webpackConfig = require("./test.webpack.config.js");
const tsconfig = require("./test.tsconfig.json");
const path = require("path");

const testRecursivePath = "test/visualTest.ts";
const srcOriginalRecursivePath = "src/**/*.ts";
const coverageFolder = "coverage";

process.env.CHROME_BIN = require("puppeteer").executablePath();

import { Config, ConfigOptions } from "karma";

module.exports = (config: Config) => {
    config.set(<ConfigOptions>{
        mode: "development",
        browserNoActivityTimeout: 100000,
        browsers: ["ChromeHeadless"], // or Chrome to use locally installed Chrome browser
        colors: true,
        frameworks: ["jasmine"],
        reporters: [
            "progress",
            "junit",
            "coverage-istanbul"
        ],
        junitReporter: {
            outputDir: path.join(__dirname, coverageFolder),
            outputFile: "TESTS-report.xml",
            useBrowserName: false
        },
        singleRun: true,
        plugins: [
            "karma-coverage",
            "karma-typescript",
            "karma-webpack",
            "karma-jasmine",
            "karma-sourcemap-loader",
            "karma-chrome-launcher",
            "karma-junit-reporter",
            "karma-coverage-istanbul-reporter"
        ],
        files: [
            "node_modules/jquery/dist/jquery.min.js",
            "node_modules/jasmine-jquery/lib/jasmine-jquery.js",
            {
                pattern: './capabilities.json',
                watched: false,
                served: true,
                included: false
            },
            testRecursivePath,
            {
                pattern: srcOriginalRecursivePath,
                included: false,
                served: true
            }
        ],
        preprocessors: {
            [testRecursivePath]: ["webpack", "coverage"]
        },
        typescriptPreprocessor: {
            options: tsconfig.compilerOptions
        },
        coverageIstanbulReporter: {
            reports: ["html", "lcovonly", "text-summary", "cobertura"],
            dir: path.join(__dirname, coverageFolder),
            'report-config': {
                html: {
                    subdir: 'html-report'
                }
            },
            combineBrowserReports: true,
            fixWebpackSourcePaths: true,
            verbose: false
        },
        coverageReporter: {
            dir: path.join(__dirname, coverageFolder),
            reporters: [
                // reporters not supporting the `file` property
                { type: 'html', subdir: 'html-report' },
                { type: 'lcov', subdir: 'lcov' },
                // reporters supporting the `file` property, use `subdir` to directly
                // output them in the `dir` directory
                { type: 'cobertura', subdir: '.', file: 'cobertura-coverage.xml' },
                { type: 'lcovonly', subdir: '.', file: 'report-lcovonly.txt' },
                { type: 'text-summary', subdir: '.', file: 'text-summary.txt' },
            ]
        },
        mime: {
            "text/x-typescript": ["ts", "tsx"]
        },
        webpack: webpackConfig,
        webpackMiddleware: {
            stats: "errors-only"
        }
    });
};

Sie können diese Konfiguration bei Bedarf ändern.If necessary, you can modify this configuration.

Der Code in karma.conf.js enthält die folgende Variablen:The code in karma.conf.js contains the following variable:

  • recursivePathToTests: der Speicherort des Testcodes.recursivePathToTests: Locates the test code

  • srcRecursivePath: der Speicherort des JavaScript-Ausgabecodes nach dem Kompilieren.srcRecursivePath: Locates the output JavaScript code after compiling

  • srcCssRecursivePath: der Speicherort der CSS-Ausgabe nach dem Kompilieren der LESS-Datei mit Formatangaben.srcCssRecursivePath: Locates the output CSS after compiling less file with styles

  • srcOriginalRecursivePath: der Speicherort des Visualquellcodes.srcOriginalRecursivePath: Locates the source code of your visual

  • coverageFolder: der Speicherort, an dem der Bericht zur Code Coverage erstellt werden soll.coverageFolder: Determines where the coverage report is to be created

Die Konfigurationsdatei enthält die folgenden Eigenschaften:The configuration file includes the following properties:

  • singleRun: true: Tests lassen sich entweder einmalig oder auf einem CI-System (Continuous Integration) ausführen.singleRun: true: Tests are run on a continuous integration (CI) system, or they can be run one time. Sie können für das Debuggen von Tests die Einstellung false festlegen.You can change the setting to false for debugging your tests. Der Browser wird von Karma weiterhin ausgeführt, damit Sie die Konsole zum Debuggen verwenden können.Karma keeps the browser running so that you can use the console for debugging.

  • files: [...]: In diesem Array können Sie die Dateien festlegen, die in den Browser geladen werden sollen.files: [...]: In this array, you can specify the files to load to the browser. Normalerweise handelt es sich um Quelldateien, Testfälle und Bibliotheken (für Jasmine und Testhilfsprogramme).Usually, there are source files, test cases, libraries (Jasmine, test utilities). Sie können die Liste bei Bedarf um weitere Dateien ergänzen.You can add additional files to the list, as necessary.

  • preprocessors: In diesem Abschnitt konfigurieren Sie die Aktionen, die vor den Komponententests ausgeführt werden.preprocessors: In this section, you configure actions that run before the unit tests run. Die Aktionen kompilieren TypeScript vorab zu JavaScript, bereiten Quellzuordnungsdateien vor und generieren den Bericht zur Code Coverage.They precompile the typescript to JavaScript, prepare source map files, and generate code coverage report. Sie können coverage deaktivieren, wenn Sie Tests debuggen.You can disable coverage when you debug your tests. Durch „coverage“ wird zusätzlicher Testcode generiert, der bei der Code Coverage für Tests berücksichtigt wird. Dies erschwert das Debuggen von Tests.Coverage generates additional code for check code for the test coverage, which complicates debugging tests.

Eine Beschreibung aller Karma-Konfigurationen finden Sie auf der Seite Karma-Konfigurationsdatei.For descriptions of all Karma configurations, go to the Karma Configuration File page.

Sie können scripts praktischerweise einen Testbefehl hinzufügen:For your convenience, you can add a test command into scripts:

{
    "scripts": {
        "pbiviz": "pbiviz",
        "start": "pbiviz start",
        "typings":"node node_modules/typings/dist/bin.js i",
        "lint": "tslint -r \"node_modules/tslint-microsoft-contrib\"  \"+(src|test)/**/*.ts\"",
        "pretest": "pbiviz package --resources --no-minify --no-pbiviz --no-plugin",
        "test": "karma start"
    }
    ...
}

Nun können Sie mit dem Erstellen von Komponententests beginnen.You're now ready to begin writing your unit tests.

Überprüfen des DOM-Elements des VisualsCheck the DOM element of the visual

Erstellen Sie zunächst eine Instanz des Visuals, um dieses zu testen.To test the visual, first create an instance of visual.

Erstellen des Visualinstanz-GeneratorsCreate a visual instance builder

Fügen Sie dem Ordner test die Datei visualBuilder.ts hinzu, indem Sie folgenden Code verwenden:Add a visualBuilder.ts file to the test folder by using the following code:

import {
    VisualBuilderBase
} from "powerbi-visuals-utils-testutils";

import {
    BarChart as VisualClass
} from "../src/visual";

import  powerbi from "powerbi-visuals-api";
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;

export class BarChartBuilder extends VisualBuilderBase<VisualClass> {
    constructor(width: number, height: number) {
        super(width, height);
    }

    protected build(options: VisualConstructorOptions) {
        return new VisualClass(options);
    }

    public get mainElement() {
        return this.element.children("svg.barChart");
    }
}

Es gibt eine build-Methode, mit der Sie eine Instanz des Visuals erstellen können.There's build method for creating an instance of your visual. mainElement ist eine get-Methode, die eine Instanz des DOM-Stammelements im Visual zurückgibt.mainElement is a get method, which returns an instance of "root" document object model (DOM) element in your visual. Der Getter ist optional, erleichtert jedoch das Erstellen von Komponententests.The getter is optional, but it makes writing the unit test easier.

Sie verfügen nun über eine Instanz des Visuals.You now have a build of an instance of your visual. Als Nächstes schreiben Sie den Testfall.Let's write the test case. Mit dem Testfall werden die SVG-Elemente überprüft, die beim Anzeigen des Visuals erstellt werden.The test case checks the SVG elements that are created when your visual is displayed.

Erstellen einer TypeScript-Datei zum Schreiben von TestfällenCreate a typescript file to write test cases

Fügen Sie den Testfällen die Datei visualTest.ts hinzu, indem Sie folgenden Code verwenden:Add a visualTest.ts file for the test cases by using the following code:

import powerbi from "powerbi-visuals-api";

import { BarChartBuilder } from "./VisualBuilder";

import {
    BarChart as VisualClass
} from "../src/visual";

import VisualBuilder = powerbi.extensibility.visual.test.BarChartBuilder;

describe("BarChart", () => {
    let visualBuilder: VisualBuilder;
    let dataView: DataView;

    beforeEach(() => {
        visualBuilder = new VisualBuilder(500, 500);
    });

    it("root DOM element is created", () => {
        expect(visualBuilder.mainElement).toBeInDOM();
    });
});

Mehrere Methoden werden aufgerufen:Several methods are called:

  • describe: beschreibt einen Testfall.describe: Describes a test case. Im Kontext des Jasmine-Frameworks wird damit häufig eine Sammlung oder eine Gruppe von Spezifikationen beschrieben.In the context of the Jasmine framework, it often describes a suite or group of specs.

  • beforeEach: wird vor jedem Aufruf der it-Methode aufgerufen, die in der Methode describe definiert ist.beforeEach: Is called before each call of the it method, which is defined in the describe method.

  • it: definiert eine einzelne Spezifikation. Die it-Methode sollte eine oder mehrere expectations enthalten.it: Defines a single spec. The it method should contain one or more expectations.

  • expect: erstellt eine Erwartung für eine Spezifikation. Eine Spezifikation ist erfolgreich, wenn alle Erwartungen ohne Fehler erfüllt werden.expect: Creates an expectation for a spec. A spec succeeds if all expectations pass without any failures.

  • toBeInDOM: eine der matchers-Methoden.toBeInDOM: One of the matchers methods. Weitere Informationen zu Matchern finden Sie unter Jasmine-Namespace: matchers.For more information about matchers, see Jasmine Namespace: matchers.

Weitere Informationen zu Jasmine finden Sie auf der Seite Dokumentation zum Jasmine-Framework.For more information about Jasmine, see the Jasmine framework documentation page.

Starten von KomponententestsLaunch unit tests

Mit diesem Test wird überprüft, ob das SVG-Stammelement der Visuals erstellt wird.This test checks that root SVG element of the visuals is created. Geben Sie den folgenden Befehl im Befehlszeilentool ein, um den Komponententest auszuführen:To run the unit test, enter the following command in the command-line tool:

npm run test

karma.js führt den Testfall im Chrome-Browser aus.karma.js runs the test case in the Chrome browser.

In Chrome geöffnetes Karma-JavaScript

Hinweis

Sie müssen Google Chrome lokal installieren.You must install Google Chrome locally.

Im Befehlszeilenfenster wird die folgende Ausgabe angezeigt:In the command-line window, you'll get following output:

> karma start

23 05 2017 12:24:26.842:WARN [watcher]: Pattern "E:/WORKSPACE/PowerBI/PowerBI-visuals-sampleBarChart/data/*.csv" does not match any file.
23 05 2017 12:24:30.836:WARN [karma]: No captured browser, open https://localhost:9876/
23 05 2017 12:24:30.849:INFO [karma]: Karma v1.3.0 server started at https://localhost:9876/
23 05 2017 12:24:30.850:INFO [launcher]: Launching browser Chrome with unlimited concurrency
23 05 2017 12:24:31.059:INFO [launcher]: Starting browser Chrome
23 05 2017 12:24:33.160:INFO [Chrome 58.0.3029 (Windows 10 0.0.0)]: Connected on socket /#2meR6hjXFmsE_fjiAAAA with id 5875251
Chrome 58.0.3029 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (0.194 secs / 0.011 secs)

=============================== Coverage summary ===============================
Statements   : 27.43% ( 65/237 )
Branches     : 19.84% ( 25/126 )
Functions    : 43.86% ( 25/57 )
Lines        : 20.85% ( 44/211 )
================================================================================

Hinzufügen statischer Daten zu KomponententestsHow to add static data for unit tests

Erstellen Sie die Datei visualData.ts im Ordner test, indem Sie folgenden Code verwenden:Create the visualData.ts file in the test folder by using the following code:

import powerbi from "powerbi-visuals-api";
import DataView = powerbi.DataView;

import {
    testDataViewBuilder,
    getRandomNumbers
} from "powerbi-visuals-utils-testutils";

export class SampleBarChartDataBuilder extends TestDataViewBuilder {
    public static CategoryColumn: string = "category";
    public static MeasureColumn: string = "measure";

    public constructor() {
        super();
        ...
    }

    public getDataView(columnNames?: string[]): DataView {
        let dateView: any = this.createCategoricalDataViewBuilder([
            ...
        ],
        [
            ...
        ], columnNames).build();

        // there's client side computed maxValue
        let maxLocal = 0;
        this.valuesMeasure.forEach((item) => {
                if (item > maxLocal) {
                    maxLocal = item;
                }
        });
        (<any>dataView).categorical.values[0].maxLocal = maxLocal;
    }
}

Die SampleBarChartDataBuilder-Klasse erbt von TestDataViewBuilder und implementiert die abstrakte Methode getDataView.The SampleBarChartDataBuilder class extends TestDataViewBuilder and implements the abstract method getDataView.

Wenn Sie Daten in Datenfeldbuckets eingeben, generiert Power BI ein dataview-Kategorieobjekt, das auf den Daten basiert.When you put data into data-field buckets, Power BI produces a categorical dataview object that's based on your data.

Datenfeldbuckets

In Komponententests stehen keine Power BI-Kernfunktionen zur Verfügung, mit denen sich die Daten kopieren lassen.In unit tests, you don't have Power BI core functions to reproduce the data. Sie müssen die statischen Daten jedoch dem dataview-Kategorieobjekt zuordnen.But you need to map your static data to the categorical dataview. Dabei unterstützt Sie die TestDataViewBuilder-Klasse.The TestDataViewBuilder class can help you map it.

Weitere Informationen zur Zuordnung von Datenansichten finden Sie unter DataViewMappings.For more information about Data View mapping, see DataViewMappings.

In der getDataView-Methode rufen Sie die createCategoricalDataViewBuilder-Methode mit Ihren Daten auf.In the getDataView method, you call the createCategoricalDataViewBuilder method with your data.

Die Datei capabilities.json des sampleBarChart-Visuals enthält das dataRoles-Objekt und das dataViewMapping-Objekt:In sampleBarChart visual capabilities.json file, we have dataRoles and dataViewMapping objects:

"dataRoles": [
    {
        "displayName": "Category Data",
        "name": "category",
        "kind": "Grouping"
    },
    {
        "displayName": "Measure Data",
        "name": "measure",
        "kind": "Measure"
    }
],
"dataViewMappings": [
    {
        "conditions": [
            {
                "category": {
                    "max": 1
                },
                "measure": {
                    "max": 1
                }
            }
        ],
        "categorical": {
            "categories": {
                "for": {
                    "in": "category"
                }
            },
            "values": {
                "select": [
                    {
                        "bind": {
                            "to": "measure"
                        }
                    }
                ]
            }
        }
    }
],

Um die gleiche Zuordnung zu generieren, müssen Sie die folgenden Parameter auf die createCategoricalDataViewBuilder-Methode festlegen:To generate the same mapping, you must set the following params to createCategoricalDataViewBuilder method:

([
    {
        source: {
            displayName: "Category",
            queryName: SampleBarChartData.ColumnCategory,
            type: ValueType.fromDescriptor({ text: true }),
            roles: {
                Category: true
            },
        },
        values: this.valuesCategory
    }
],
[
    {
        source: {
            displayName: "Measure",
            isMeasure: true,
            queryName: SampleBarChartData.MeasureColumn,
            type: ValueType.fromDescriptor({ numeric: true }),
            roles: {
                Measure: true
            },
        },
        values: this.valuesMeasure
    },
], columnNames)

Dabei ist this.valuesCategory ein Array von Kategorien:Where this.valuesCategory is an array of categories:

public valuesCategory: string[] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];

this.valuesMeasure ist ein Array von Measures für jede Kategorie:And this.valuesMeasure is an array of measures for each category:

public valuesMeasure: number[] = [742731.43, 162066.43, 283085.78, 300263.49, 376074.57, 814724.34, 570921.34];

Jetzt können Sie die SampleBarChartDataBuilder-Klasse im Komponententest verwenden.Now, you can use the SampleBarChartDataBuilder class in your unit test.

Die ValueType-Klasse ist im Paket „powerbi-visuals-utils-testutils“ definiert.The ValueType class is defined in the powerbi-visuals-utils-testutils package. Für die createCategoricalDataViewBuilder-Methode ist die lodash-Bibliothek erforderlich.And the createCategoricalDataViewBuilder method requires the lodash library.

Fügen Sie die folgenden Pakete den Abhängigkeiten hinzu.Add these packages to the dependencies.

In package.json im Abschnitt devDependenciesIn package.json at devDependencies section

"lodash-es": "4.17.1",
"powerbi-visuals-utils-testutils": "2.2.0"

AufrufCall

npm install

zur Installation der lodash-es-Bibliothekto install lodash-es library.

Nun können Sie den Komponententest erneut ausführen.Now, you can run the unit test again. Folgende Ausgabe muss angezeigt werden:You must get the following output:

> karma start

23 05 2017 16:19:54.318:WARN [watcher]: Pattern "E:/WORKSPACE/PowerBI/PowerBI-visuals-sampleBarChart/data/*.csv" does not match any file.
23 05 2017 16:19:58.333:WARN [karma]: No captured browser, open https://localhost:9876/
23 05 2017 16:19:58.346:INFO [karma]: Karma v1.3.0 server started at https://localhost:9876/
23 05 2017 16:19:58.346:INFO [launcher]: Launching browser Chrome with unlimited concurrency
23 05 2017 16:19:58.394:INFO [launcher]: Starting browser Chrome
23 05 2017 16:19:59.873:INFO [Chrome 58.0.3029 (Windows 10 0.0.0)]: Connected on socket /#NcNTAGH9hWfGMCuEAAAA with id 3551106
Chrome 58.0.3029 (Windows 10 0.0.0): Executed 1 of 1 SUCCESS (1.266 secs / 1.052 secs)

=============================== Coverage summary ===============================
Statements   : 56.72% ( 135/238 )
Branches     : 32.54% ( 41/126 )
Functions    : 66.67% ( 38/57 )
Lines        : 52.83% ( 112/212 )
================================================================================

Das Visual wird im Chrome-Browser geöffnet und wie folgt dargestellt:Your visual opens in the Chrome browser, as shown:

Komponententest wird in Chrome gestartet.

Aus der Zusammenfassung geht hervor, dass sich die Code Coverage erhöht hat.The summary shows that coverage has increased. Weitere Informationen zur aktuellen Code Coverage finden Sie in der Datei coverage\index.html.To learn more about current code coverage, open coverage\index.html.

Code Coverage-Index des Komponententests

Sie können sich auch die Code Coverage für den Ordner src ansehen:Or look at the scope of the src folder:

Code Coverage für Ordner „src“

Sie können sich für jede Datei den Quellcode anzeigen lassen.In the scope of file, you can view the source code. Die Coverage-Hilfsprogramme heben Zeilen rot hervor, wenn bestimmter Code während der Komponententests nicht ausgeführt wird.The Coverage utilities would highlight the row in red if certain code isn't executed during the unit tests.

Code Coverage der Datei „visual.ts“

Wichtig

Die Code Coverage sagt nichts darüber aus, ob ein Großteil der Visualfunktionen getestet wurde.Code coverage doesn't mean that you have good functionality coverage of the visual. Ein einfacher Komponententest führt zu einer Code Coverage von über 96 % in src\visual.ts.One simple unit test provides over 96 percent coverage in src\visual.ts.

Nächste SchritteNext steps

Sobald Ihr Visual bereit ist, können Sie es zur Veröffentlichung übermitteln.When your visual is ready, you can submit it for publication. Weitere Informationen finden Sie unter Veröffentlichen von Power BI-Visuals in AppSource.For more information, see Publish Power BI visuals to AppSource.