summaryrefslogtreecommitdiff
path: root/spec/frontend
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-08 09:09:39 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-08 09:09:39 +0000
commit5bdbc604c8a08f827c3833e2c28ec0c299bb41fc (patch)
tree1fbf5e5dc3bff54d371baa15775371ebc8525f62 /spec/frontend
parent79ddf163588de2d9a7f1cc27262dc1a14503f619 (diff)
downloadgitlab-ce-5bdbc604c8a08f827c3833e2c28ec0c299bb41fc.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/design_management/router_spec.js79
-rw-r--r--spec/frontend/design_management/utils/tracking_spec.js51
-rw-r--r--spec/frontend/diffs/components/diff_content_spec.js2
-rw-r--r--spec/frontend/notebook/cells/code_spec.js74
-rw-r--r--spec/frontend/notebook/cells/markdown_spec.js105
-rw-r--r--spec/frontend/notebook/cells/output/html_sanitize_tests.js68
-rw-r--r--spec/frontend/notebook/cells/output/html_spec.js31
-rw-r--r--spec/frontend/notebook/cells/output/index_spec.js115
-rw-r--r--spec/frontend/notebook/cells/prompt_spec.js56
-rw-r--r--spec/frontend/notebook/index_spec.js100
10 files changed, 637 insertions, 44 deletions
diff --git a/spec/frontend/design_management/router_spec.js b/spec/frontend/design_management/router_spec.js
index fc88bfa06d2..0f4afa5e288 100644
--- a/spec/frontend/design_management/router_spec.js
+++ b/spec/frontend/design_management/router_spec.js
@@ -1,4 +1,5 @@
import { mount, createLocalVue } from '@vue/test-utils';
+import { nextTick } from 'vue';
import VueRouter from 'vue-router';
import App from '~/design_management/components/app.vue';
import Designs from '~/design_management/pages/index.vue';
@@ -11,74 +12,66 @@ import {
} from '~/design_management/router/constants';
import '~/commons/bootstrap';
-jest.mock('mousetrap', () => ({
- bind: jest.fn(),
- unbind: jest.fn(),
-}));
-
-describe('Design management router', () => {
- let vm;
- let router;
-
- function factory() {
- const localVue = createLocalVue();
-
- localVue.use(VueRouter);
+function factory(routeArg) {
+ const localVue = createLocalVue();
+ localVue.use(VueRouter);
- window.gon = { sprite_icons: '' };
+ window.gon = { sprite_icons: '' };
- router = createRouter('/');
+ const router = createRouter('/');
+ if (routeArg !== undefined) {
+ router.push(routeArg);
+ }
- vm = mount(App, {
- localVue,
- router,
- mocks: {
- $apollo: {
- queries: {
- designs: { loading: true },
- design: { loading: true },
- permissions: { loading: true },
- },
+ return mount(App, {
+ localVue,
+ router,
+ mocks: {
+ $apollo: {
+ queries: {
+ designs: { loading: true },
+ design: { loading: true },
+ permissions: { loading: true },
},
},
- });
- }
-
- beforeEach(() => {
- factory();
+ },
});
+}
- afterEach(() => {
- vm.destroy();
+jest.mock('mousetrap', () => ({
+ bind: jest.fn(),
+ unbind: jest.fn(),
+}));
- router.app.$destroy();
+describe('Design management router', () => {
+ afterEach(() => {
window.location.hash = '';
});
- describe.each([['/'], [{ name: ROOT_ROUTE_NAME }]])('root route', pushArg => {
+ describe.each([['/'], [{ name: ROOT_ROUTE_NAME }]])('root route', routeArg => {
it('pushes home component', () => {
- router.push(pushArg);
+ const wrapper = factory(routeArg);
- expect(vm.find(Designs).exists()).toBe(true);
+ expect(wrapper.find(Designs).exists()).toBe(true);
});
});
- describe.each([['/designs'], [{ name: DESIGNS_ROUTE_NAME }]])('designs route', pushArg => {
+ describe.each([['/designs'], [{ name: DESIGNS_ROUTE_NAME }]])('designs route', routeArg => {
it('pushes designs root component', () => {
- router.push(pushArg);
+ const wrapper = factory(routeArg);
- expect(vm.find(Designs).exists()).toBe(true);
+ expect(wrapper.find(Designs).exists()).toBe(true);
});
});
describe.each([['/designs/1'], [{ name: DESIGN_ROUTE_NAME, params: { id: '1' } }]])(
'designs detail route',
- pushArg => {
+ routeArg => {
it('pushes designs detail component', () => {
- router.push(pushArg);
+ const wrapper = factory(routeArg);
- return vm.vm.$nextTick().then(() => {
- const detail = vm.find(DesignDetail);
+ return nextTick().then(() => {
+ const detail = wrapper.find(DesignDetail);
expect(detail.exists()).toBe(true);
expect(detail.props('id')).toEqual('1');
});
diff --git a/spec/frontend/design_management/utils/tracking_spec.js b/spec/frontend/design_management/utils/tracking_spec.js
new file mode 100644
index 00000000000..ab540587c01
--- /dev/null
+++ b/spec/frontend/design_management/utils/tracking_spec.js
@@ -0,0 +1,51 @@
+import { mockTracking } from 'helpers/tracking_helper';
+import { trackDesignDetailView } from '~/design_management/utils/tracking';
+
+function getTrackingSpy(key) {
+ return mockTracking(key, undefined, jest.spyOn);
+}
+
+describe('Tracking Events', () => {
+ describe('trackDesignDetailView', () => {
+ const eventKey = 'projects:issues:design';
+ const eventName = 'design_viewed';
+
+ it('trackDesignDetailView fires a tracking event when called', () => {
+ const trackingSpy = getTrackingSpy(eventKey);
+
+ trackDesignDetailView();
+
+ expect(trackingSpy).toHaveBeenCalledWith(
+ eventKey,
+ eventName,
+ expect.objectContaining({
+ label: eventName,
+ value: {
+ 'internal-object-refrerer': '',
+ 'version-number': 1,
+ 'current-version': false,
+ },
+ }),
+ );
+ });
+
+ it('trackDesignDetailView allows to customize the value payload', () => {
+ const trackingSpy = getTrackingSpy(eventKey);
+
+ trackDesignDetailView('from-a-test', 100, true);
+
+ expect(trackingSpy).toHaveBeenCalledWith(
+ eventKey,
+ eventName,
+ expect.objectContaining({
+ label: eventName,
+ value: {
+ 'internal-object-refrerer': 'from-a-test',
+ 'version-number': 100,
+ 'current-version': true,
+ },
+ }),
+ );
+ });
+ });
+});
diff --git a/spec/frontend/diffs/components/diff_content_spec.js b/spec/frontend/diffs/components/diff_content_spec.js
index 979c67787f7..b78895f9e55 100644
--- a/spec/frontend/diffs/components/diff_content_spec.js
+++ b/spec/frontend/diffs/components/diff_content_spec.js
@@ -10,7 +10,7 @@ import ImageDiffOverlay from '~/diffs/components/image_diff_overlay.vue';
import NoteForm from '~/notes/components/note_form.vue';
import DiffDiscussions from '~/diffs/components/diff_discussions.vue';
import { IMAGE_DIFF_POSITION_TYPE } from '~/diffs/constants';
-import diffFileMockData from '../../../javascripts/diffs/mock_data/diff_file';
+import diffFileMockData from '../mock_data/diff_file';
import { diffViewerModes } from '~/ide/constants';
const localVue = createLocalVue();
diff --git a/spec/frontend/notebook/cells/code_spec.js b/spec/frontend/notebook/cells/code_spec.js
new file mode 100644
index 00000000000..3c9aea4a61a
--- /dev/null
+++ b/spec/frontend/notebook/cells/code_spec.js
@@ -0,0 +1,74 @@
+import Vue from 'vue';
+import CodeComponent from '~/notebook/cells/code.vue';
+
+const Component = Vue.extend(CodeComponent);
+
+describe('Code component', () => {
+ let vm;
+ let json;
+
+ beforeEach(() => {
+ json = getJSONFixture('blob/notebook/basic.json');
+ });
+
+ const setupComponent = cell => {
+ const comp = new Component({
+ propsData: {
+ cell,
+ },
+ });
+ comp.$mount();
+ return comp;
+ };
+
+ describe('without output', () => {
+ beforeEach(done => {
+ vm = setupComponent(json.cells[0]);
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('does not render output prompt', () => {
+ expect(vm.$el.querySelectorAll('.prompt').length).toBe(1);
+ });
+ });
+
+ describe('with output', () => {
+ beforeEach(done => {
+ vm = setupComponent(json.cells[2]);
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('does not render output prompt', () => {
+ expect(vm.$el.querySelectorAll('.prompt').length).toBe(2);
+ });
+
+ it('renders output cell', () => {
+ expect(vm.$el.querySelector('.output')).toBeDefined();
+ });
+ });
+
+ describe('with string for cell.source', () => {
+ beforeEach(done => {
+ const cell = json.cells[0];
+ cell.source = cell.source.join('');
+
+ vm = setupComponent(cell);
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('renders the same input as when cell.source is an array', () => {
+ const expected = "console.log('test')";
+
+ expect(vm.$el.querySelector('.input').innerText).toContain(expected);
+ });
+ });
+});
diff --git a/spec/frontend/notebook/cells/markdown_spec.js b/spec/frontend/notebook/cells/markdown_spec.js
new file mode 100644
index 00000000000..359ac90a3ef
--- /dev/null
+++ b/spec/frontend/notebook/cells/markdown_spec.js
@@ -0,0 +1,105 @@
+import Vue from 'vue';
+import katex from 'katex';
+import MarkdownComponent from '~/notebook/cells/markdown.vue';
+
+const Component = Vue.extend(MarkdownComponent);
+
+window.katex = katex;
+
+describe('Markdown component', () => {
+ let vm;
+ let cell;
+ let json;
+
+ beforeEach(done => {
+ json = getJSONFixture('blob/notebook/basic.json');
+
+ // eslint-disable-next-line prefer-destructuring
+ cell = json.cells[1];
+
+ vm = new Component({
+ propsData: {
+ cell,
+ },
+ });
+ vm.$mount();
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('does not render promot', () => {
+ expect(vm.$el.querySelector('.prompt span')).toBeNull();
+ });
+
+ it('does not render the markdown text', () => {
+ expect(vm.$el.querySelector('.markdown').innerHTML.trim()).not.toEqual(cell.source.join(''));
+ });
+
+ it('renders the markdown HTML', () => {
+ expect(vm.$el.querySelector('.markdown h1')).not.toBeNull();
+ });
+
+ it('sanitizes output', done => {
+ Object.assign(cell, {
+ source: [
+ '[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n',
+ ],
+ });
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('a').getAttribute('href')).toBeNull();
+
+ done();
+ });
+ });
+
+ describe('katex', () => {
+ beforeEach(() => {
+ json = getJSONFixture('blob/notebook/math.json');
+ });
+
+ it('renders multi-line katex', done => {
+ vm = new Component({
+ propsData: {
+ cell: json.cells[0],
+ },
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.katex')).not.toBeNull();
+
+ done();
+ });
+ });
+
+ it('renders inline katex', done => {
+ vm = new Component({
+ propsData: {
+ cell: json.cells[1],
+ },
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('p:first-child .katex')).not.toBeNull();
+
+ done();
+ });
+ });
+
+ it('renders multiple inline katex', done => {
+ vm = new Component({
+ propsData: {
+ cell: json.cells[1],
+ },
+ }).$mount();
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelectorAll('p:nth-child(2) .katex').length).toBe(4);
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/notebook/cells/output/html_sanitize_tests.js b/spec/frontend/notebook/cells/output/html_sanitize_tests.js
new file mode 100644
index 00000000000..74c48f04367
--- /dev/null
+++ b/spec/frontend/notebook/cells/output/html_sanitize_tests.js
@@ -0,0 +1,68 @@
+export default {
+ 'protocol-based JS injection: simple, no spaces': {
+ input: '<a href="javascript:alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: simple, spaces before': {
+ input: '<a href="javascript :alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: simple, spaces after': {
+ input: '<a href="javascript: alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: simple, spaces before and after': {
+ input: '<a href="javascript : alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: preceding colon': {
+ input: '<a href=":javascript:alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: UTF-8 encoding': {
+ input: '<a href="javascript&#58;">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: long UTF-8 encoding': {
+ input: '<a href="javascript&#0058;">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: long UTF-8 encoding without semicolons': {
+ input:
+ '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: hex encoding': {
+ input: '<a href="javascript&#x3A;">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: long hex encoding': {
+ input: '<a href="javascript&#x003A;">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: hex encoding without semicolons': {
+ input:
+ '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: null char': {
+ input: '<a href=java\0script:alert("XSS")>foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: invalid URL char': {
+ input: '<img src=javascript:alert("XSS")>',
+ output: '<img>',
+ },
+ 'protocol-based JS injection: Unicode': {
+ input: '<a href="\u0001java\u0003script:alert(\'XSS\')">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'protocol-based JS injection: spaces and entities': {
+ input: '<a href=" &#14; javascript:alert(\'XSS\');">foo</a>',
+ output: '<a>foo</a>',
+ },
+ 'img on error': {
+ input: '<img src="x" onerror="alert(document.domain)" />',
+ output: '<img src="x">',
+ },
+};
diff --git a/spec/frontend/notebook/cells/output/html_spec.js b/spec/frontend/notebook/cells/output/html_spec.js
new file mode 100644
index 00000000000..3ee404fb187
--- /dev/null
+++ b/spec/frontend/notebook/cells/output/html_spec.js
@@ -0,0 +1,31 @@
+import Vue from 'vue';
+import htmlOutput from '~/notebook/cells/output/html.vue';
+import sanitizeTests from './html_sanitize_tests';
+
+describe('html output cell', () => {
+ function createComponent(rawCode) {
+ const Component = Vue.extend(htmlOutput);
+
+ return new Component({
+ propsData: {
+ rawCode,
+ count: 0,
+ index: 0,
+ },
+ }).$mount();
+ }
+
+ describe('sanitizes output', () => {
+ Object.keys(sanitizeTests).forEach(key => {
+ it(key, () => {
+ const test = sanitizeTests[key];
+ const vm = createComponent(test.input);
+ const outputEl = [...vm.$el.querySelectorAll('div')].pop();
+
+ expect(outputEl.innerHTML).toEqual(test.output);
+
+ vm.$destroy();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/notebook/cells/output/index_spec.js b/spec/frontend/notebook/cells/output/index_spec.js
new file mode 100644
index 00000000000..2b1aa5317c5
--- /dev/null
+++ b/spec/frontend/notebook/cells/output/index_spec.js
@@ -0,0 +1,115 @@
+import Vue from 'vue';
+import CodeComponent from '~/notebook/cells/output/index.vue';
+
+const Component = Vue.extend(CodeComponent);
+
+describe('Output component', () => {
+ let vm;
+ let json;
+
+ const createComponent = output => {
+ vm = new Component({
+ propsData: {
+ outputs: [].concat(output),
+ count: 1,
+ },
+ });
+ vm.$mount();
+ };
+
+ beforeEach(() => {
+ json = getJSONFixture('blob/notebook/basic.json');
+ });
+
+ describe('text output', () => {
+ beforeEach(done => {
+ createComponent(json.cells[2].outputs[0]);
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('renders as plain text', () => {
+ expect(vm.$el.querySelector('pre')).not.toBeNull();
+ });
+
+ it('renders promot', () => {
+ expect(vm.$el.querySelector('.prompt span')).not.toBeNull();
+ });
+ });
+
+ describe('image output', () => {
+ beforeEach(done => {
+ createComponent(json.cells[3].outputs[0]);
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('renders as an image', () => {
+ expect(vm.$el.querySelector('img')).not.toBeNull();
+ });
+ });
+
+ describe('html output', () => {
+ it('renders raw HTML', () => {
+ createComponent(json.cells[4].outputs[0]);
+
+ expect(vm.$el.querySelector('p')).not.toBeNull();
+ expect(vm.$el.querySelectorAll('p').length).toBe(1);
+ expect(vm.$el.textContent.trim()).toContain('test');
+ });
+
+ it('renders multiple raw HTML outputs', () => {
+ createComponent([json.cells[4].outputs[0], json.cells[4].outputs[0]]);
+
+ expect(vm.$el.querySelectorAll('p').length).toBe(2);
+ });
+ });
+
+ describe('svg output', () => {
+ beforeEach(done => {
+ createComponent(json.cells[5].outputs[0]);
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('renders as an svg', () => {
+ expect(vm.$el.querySelector('svg')).not.toBeNull();
+ });
+ });
+
+ describe('default to plain text', () => {
+ beforeEach(done => {
+ createComponent(json.cells[6].outputs[0]);
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('renders as plain text', () => {
+ expect(vm.$el.querySelector('pre')).not.toBeNull();
+ expect(vm.$el.textContent.trim()).toContain('testing');
+ });
+
+ it('renders promot', () => {
+ expect(vm.$el.querySelector('.prompt span')).not.toBeNull();
+ });
+
+ it("renders as plain text when doesn't recognise other types", done => {
+ createComponent(json.cells[7].outputs[0]);
+
+ setImmediate(() => {
+ expect(vm.$el.querySelector('pre')).not.toBeNull();
+ expect(vm.$el.textContent.trim()).toContain('testing');
+
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/notebook/cells/prompt_spec.js b/spec/frontend/notebook/cells/prompt_spec.js
new file mode 100644
index 00000000000..cf5a7a603c6
--- /dev/null
+++ b/spec/frontend/notebook/cells/prompt_spec.js
@@ -0,0 +1,56 @@
+import Vue from 'vue';
+import PromptComponent from '~/notebook/cells/prompt.vue';
+
+const Component = Vue.extend(PromptComponent);
+
+describe('Prompt component', () => {
+ let vm;
+
+ describe('input', () => {
+ beforeEach(done => {
+ vm = new Component({
+ propsData: {
+ type: 'In',
+ count: 1,
+ },
+ });
+ vm.$mount();
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('renders in label', () => {
+ expect(vm.$el.textContent.trim()).toContain('In');
+ });
+
+ it('renders count', () => {
+ expect(vm.$el.textContent.trim()).toContain('1');
+ });
+ });
+
+ describe('output', () => {
+ beforeEach(done => {
+ vm = new Component({
+ propsData: {
+ type: 'Out',
+ count: 1,
+ },
+ });
+ vm.$mount();
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('renders in label', () => {
+ expect(vm.$el.textContent.trim()).toContain('Out');
+ });
+
+ it('renders count', () => {
+ expect(vm.$el.textContent.trim()).toContain('1');
+ });
+ });
+});
diff --git a/spec/frontend/notebook/index_spec.js b/spec/frontend/notebook/index_spec.js
new file mode 100644
index 00000000000..36b092be976
--- /dev/null
+++ b/spec/frontend/notebook/index_spec.js
@@ -0,0 +1,100 @@
+import Vue from 'vue';
+import Notebook from '~/notebook/index.vue';
+
+const Component = Vue.extend(Notebook);
+
+describe('Notebook component', () => {
+ let vm;
+ let json;
+ let jsonWithWorksheet;
+
+ beforeEach(() => {
+ json = getJSONFixture('blob/notebook/basic.json');
+ jsonWithWorksheet = getJSONFixture('blob/notebook/worksheets.json');
+ });
+
+ describe('without JSON', () => {
+ beforeEach(done => {
+ vm = new Component({
+ propsData: {
+ notebook: {},
+ },
+ });
+ vm.$mount();
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('does not render', () => {
+ expect(vm.$el.tagName).toBeUndefined();
+ });
+ });
+
+ describe('with JSON', () => {
+ beforeEach(done => {
+ vm = new Component({
+ propsData: {
+ notebook: json,
+ codeCssClass: 'js-code-class',
+ },
+ });
+ vm.$mount();
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('renders cells', () => {
+ expect(vm.$el.querySelectorAll('.cell').length).toBe(json.cells.length);
+ });
+
+ it('renders markdown cell', () => {
+ expect(vm.$el.querySelector('.markdown')).not.toBeNull();
+ });
+
+ it('renders code cell', () => {
+ expect(vm.$el.querySelector('pre')).not.toBeNull();
+ });
+
+ it('add code class to code blocks', () => {
+ expect(vm.$el.querySelector('.js-code-class')).not.toBeNull();
+ });
+ });
+
+ describe('with worksheets', () => {
+ beforeEach(done => {
+ vm = new Component({
+ propsData: {
+ notebook: jsonWithWorksheet,
+ codeCssClass: 'js-code-class',
+ },
+ });
+ vm.$mount();
+
+ setImmediate(() => {
+ done();
+ });
+ });
+
+ it('renders cells', () => {
+ expect(vm.$el.querySelectorAll('.cell').length).toBe(
+ jsonWithWorksheet.worksheets[0].cells.length,
+ );
+ });
+
+ it('renders markdown cell', () => {
+ expect(vm.$el.querySelector('.markdown')).not.toBeNull();
+ });
+
+ it('renders code cell', () => {
+ expect(vm.$el.querySelector('pre')).not.toBeNull();
+ });
+
+ it('add code class to code blocks', () => {
+ expect(vm.$el.querySelector('.js-code-class')).not.toBeNull();
+ });
+ });
+});