Remove Ivy Codemirror (#14659)

* setup

* fix mode issue

* actions

* readonly styling

* remove ivycodemirror from package json

* wip

* test coverage for json editor

* text fixes

* fix tests

* fix cursor issue

* changelog

* clean up

* fix

* address pr comments

* unused css and it overides other styling.

* fix

* fix comment
This commit is contained in:
Angel Garbarino 2022-03-29 10:25:16 -06:00 committed by GitHub
parent 33888c3340
commit 0455d31b84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 232 additions and 187 deletions

3
changelog/14659.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
ui: Replaces the IvyCodemirror wrapper with a custom ember modifier.
```

View File

@ -14,50 +14,24 @@ import { action } from '@ember/object';
* @param {Function} [valueUpdated] - action to preform when you edit the codemirror value. * @param {Function} [valueUpdated] - action to preform when you edit the codemirror value.
* @param {Function} [onFocusOut] - action to preform when you focus out of codemirror. * @param {Function} [onFocusOut] - action to preform when you focus out of codemirror.
* @param {string} [helpText] - helper text. * @param {string} [helpText] - helper text.
* @param {object} [options] - option object that overrides codemirror default options such as the styling. * @param {Object} [extraKeys] - Provides keyboard shortcut methods for things like saving on shift + enter.
* @param {Array} [gutters] - An array of CSS class names or class name / CSS string pairs, each of which defines a width (and optionally a background), and which will be used to draw the background of the gutters.
* @param {string} [mode] - The mode defined for styling. Right now we only import ruby so mode must but be ruby or defaults to javascript. If you wanted another language you need to import it into the modifier.
* @param {Boolean} [readOnly] - Sets the view to readOnly, allowing for copying but no editing. It also hides the cursor. Defaults to false.
* @param {String} [theme] - Specify or customize the look via a named "theme" class in scss.
* @param {String} [value] - Value within the display. Generally, a json string.
* @param {String} [viewportMargin] - Size of viewport. Often set to "Infinity" to load/show all text regardless of length.
*/ */
const JSON_EDITOR_DEFAULTS = {
// IMPORTANT: `gutters` must come before `lint` since the presence of
// `gutters` is cached internally when `lint` is toggled
gutters: ['CodeMirror-lint-markers'],
tabSize: 2,
mode: 'application/json',
lineNumbers: true,
lint: { lintOnChange: false },
theme: 'hashi',
readOnly: false,
showCursorWhenSelecting: true,
};
export default class JsonEditorComponent extends Component { export default class JsonEditorComponent extends Component {
value = null;
valueUpdated = null;
onFocusOut = null;
readOnly = false;
options = null;
constructor() {
super(...arguments);
this.options = { ...JSON_EDITOR_DEFAULTS, ...this.args.options };
if (this.options.autoHeight) {
this.options.viewportMargin = Infinity;
delete this.options.autoHeight;
}
if (this.options.readOnly) {
this.options.readOnly = 'nocursor';
this.options.lineNumbers = false;
delete this.options.gutters;
}
}
get getShowToolbar() { get getShowToolbar() {
return this.args.showToolbar === false ? false : true; return this.args.showToolbar === false ? false : true;
} }
@action @action
updateValue(...args) { onUpdate(...args) {
if (this.args.valueUpdated) { if (!this.args.readOnly) {
// catching a situation in which the user is not readOnly and has not provided a valueUpdated function to the instance
this.args.valueUpdated(...args); this.args.valueUpdated(...args);
} }
} }

View File

@ -0,0 +1,68 @@
import { action } from '@ember/object';
import { bind } from '@ember/runloop';
import codemirror from 'codemirror';
import Modifier from 'ember-modifier';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/selection/active-line';
import 'codemirror/addon/lint/lint.js';
import 'codemirror/addon/lint/json-lint.js';
// right now we only use the ruby and javascript, if you use another mode you'll need to import it.
// https://codemirror.net/mode/
import 'codemirror/mode/ruby/ruby';
import 'codemirror/mode/javascript/javascript';
export default class CodeMirrorModifier extends Modifier {
didInstall() {
this._setup();
}
didUpdateArguments() {
this._editor.setOption('readOnly', this.args.named.readOnly);
if (!this.args.named.content) {
return;
}
if (this._editor.getValue() !== this.args.named.content) {
this._editor.setValue(this.args.named.content);
}
}
@action
_onChange(editor) {
this.args.named.onUpdate(editor.getValue(), this._editor);
}
@action
_onFocus(editor) {
this.args.named.onFocus(editor.getValue());
}
_setup() {
if (!this.element) {
throw new Error('CodeMirror modifier has no element');
}
const editor = codemirror(this.element, {
// IMPORTANT: `gutters` must come before `lint` since the presence of
// `gutters` is cached internally when `lint` is toggled
gutters: this.args.named.gutters || ['CodeMirror-lint-markers'],
matchBrackets: true,
lint: { lintOnChange: true },
showCursorWhenSelecting: true,
styleActiveLine: true,
tabSize: 2,
// all values we can pass into the JsonEditor
extraKeys: this.args.named.extraKeys || '',
lineNumbers: this.args.named.lineNumbers,
mode: this.args.named.mode || 'application/json',
readOnly: this.args.named.readOnly || false,
theme: this.args.named.theme || 'hashi',
value: this.args.named.content || '',
viewportMargin: this.args.named.viewportMargin || '',
});
editor.on('change', bind(this, this._onChange));
editor.on('focus', bind(this, this._onFocus));
this._editor = editor;
}
}

View File

@ -51,7 +51,7 @@ $gutter-grey: #2a2f36;
.cm-s-hashi { .cm-s-hashi {
&.CodeMirror { &.CodeMirror {
background-color: $black !important; background-color: $black !important;
resize: vertical; resize: vertical;
color: #cfd2d1 !important; color: #cfd2d1 !important;
border: none; border: none;
font-family: $family-monospace; font-family: $family-monospace;
@ -168,34 +168,14 @@ $gutter-grey: #2a2f36;
} }
.readonly-codemirror { .readonly-codemirror {
.CodeMirror-cursors { .CodeMirror-code {
cursor: default;
}
.CodeMirror-cursor {
// https://github.com/codemirror/CodeMirror/issues/1099
display: none; display: none;
} }
.cm-s-hashi {
span {
color: $light-grey;
}
span.cm-string,
span.cm-string-2 {
color: $faded-gray;
}
span.cm-number {
color: lighten($dark-gray, 30%);
}
span.cm-property {
color: white;
}
span.cm-variable-2 {
color: $light-grey-blue;
}
}
} }
.cm-s-auto-height.CodeMirror { .cm-s-auto-height.CodeMirror {
height: auto; height: auto;
} }

View File

@ -2,7 +2,10 @@
<JsonEditor <JsonEditor
@showToolbar={{false}} @showToolbar={{false}}
@value={{stringify this.content}} @value={{stringify this.content}}
@options={{hash readOnly=true lineNumbers=false autoHeight=true gutters=false theme="hashi auto-height"}} @readOnly={{true}}
@viewportMargin="Infinity"
@gutters={{false}}
@theme="hashi auto-height"
/> />
<HoverCopyButton @copyValue={{stringify this.content}} /> <HoverCopyButton @copyValue={{stringify this.content}} />
</div> </div>

View File

@ -17,7 +17,10 @@
data-test-json-viewer data-test-json-viewer
@showToolbar={{false}} @showToolbar={{false}}
@value={{stringify this.unwrapData}} @value={{stringify this.unwrapData}}
@options={{hash readOnly=true lineNumbers=false autoHeight=true gutters=false theme="hashi-read-only auto-height"}} @readOnly={{true}}
@viewportMargin="Infinity"
@gutters={{false}}
@theme="hashi-read-only auto-height"
/> />
<HoverCopyButton @copyValue={{stringify this.unwrapData}} /> <HoverCopyButton @copyValue={{stringify this.unwrapData}} />
</div> </div>

View File

@ -23,14 +23,22 @@
</Toolbar> </Toolbar>
</div> </div>
{{/if}} {{/if}}
<div
<IvyCodemirror {{code-mirror
@data-test-component="json-editor" content=@value
@value={{@value}} extraKeys=@extraKeys
@options={{this.options}} gutters=@gutters
@valueUpdated={{action "updateValue"}} lineNumbers=(if @readOnly false true)
@onFocusOut={{action "onFocus"}} mode=@mode
/> readOnly=@readOnly
theme=@theme
viewportMarg=@viewportMargin
onUpdate=this.onUpdate
onFocus=this.onFocus
}}
class={{if @readOnly "readonly-codemirror"}}
data-test-component="code-mirror-modifier"
></div>
{{#if @helpText}} {{#if @helpText}}
<div class="box is-shadowless is-fullwidth has-short-padding"> <div class="box is-shadowless is-fullwidth has-short-padding">

View File

@ -33,7 +33,7 @@
<JsonEditor <JsonEditor
@title={{if @isV2 "Version Data" "Secret Data"}} @title={{if @isV2 "Version Data" "Secret Data"}}
@value={{@modelForData.dataAsJSONString}} @value={{@modelForData.dataAsJSONString}}
@options={{hash readOnly=true}} @readOnly={{true}}
/> />
</div> </div>
{{else}} {{else}}

View File

@ -45,7 +45,7 @@
{{#if (eq @unwrapActiveTab "data")}} {{#if (eq @unwrapActiveTab "data")}}
<div class="field"> <div class="field">
<div class="control"> <div class="control">
<JsonEditor @title="Unwrapped Data" @value={{stringify @unwrap_data}} @options={{hash readOnly=true}} /> <JsonEditor @title="Unwrapped Data" @value={{stringify @unwrap_data}} @readOnly={{true}} />
</div> </div>
</div> </div>
{{else}} {{else}}

View File

@ -8,7 +8,7 @@
<JsonEditor <JsonEditor
@title="Ciphertext" @title="Ciphertext"
@valueUpdated={{action (mut @ciphertext)}} @valueUpdated={{action (mut @ciphertext)}}
@options={{hash mode="ruby"}} @mode="ruby"
@data-test-transit-input="ciphertext" @data-test-transit-input="ciphertext"
/> />
</div> </div>

View File

@ -16,7 +16,7 @@
@title="Plaintext" @title="Plaintext"
@value={{@plaintext}} @value={{@plaintext}}
@valueUpdated={{action (mut @plaintext)}} @valueUpdated={{action (mut @plaintext)}}
@options={{hash mode="ruby"}} @mode="ruby"
@data-test-transit-input="plaintext" @data-test-transit-input="plaintext"
/> />
</div> </div>

View File

@ -11,12 +11,7 @@
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} /> <KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
<div class="field"> <div class="field">
<div id="input-control" class="control is-relative"> <div id="input-control" class="control is-relative">
<JsonEditor <JsonEditor @title="Input" @valueUpdated={{action (mut @input)}} @mode="ruby" @data-test-transit-input="input" />
@title="Input"
@valueUpdated={{action (mut @input)}}
@options={{hash mode="ruby"}}
@data-test-transit-input="input"
/>
</div> </div>
</div> </div>
<div class="field"> <div class="field">

View File

@ -11,7 +11,7 @@
<KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} /> <KeyVersionSelect @key={{@key}} @onVersionChange={{action (mut @key_version)}} @key_version={{@key_version}} />
<div class="field"> <div class="field">
<div class="control is-expanded"> <div class="control is-expanded">
<JsonEditor @title="Ciphertext" @valueUpdated={{action (mut @ciphertext)}} @options={{hash mode="ruby"}} /> <JsonEditor @title="Ciphertext" @valueUpdated={{action (mut @ciphertext)}} @mode="ruby" />
</div> </div>
</div> </div>
{{#if @key.derived}} {{#if @key.derived}}

View File

@ -28,7 +28,7 @@
@title="Input" @title="Input"
@value={{@input}} @value={{@input}}
@valueUpdated={{action (mut @input)}} @valueUpdated={{action (mut @input)}}
@options={{hash mode="ruby"}} @mode="ruby"
@data-test-transit-input="input" @data-test-transit-input="input"
/> />
</div> </div>

View File

@ -23,7 +23,7 @@
@title="Input" @title="Input"
@value={{@input}} @value={{@input}}
@valueUpdated={{action (mut @input)}} @valueUpdated={{action (mut @input)}}
@options={{hash mode="ruby"}} @mode="ruby"
@data-test-transit-input="input" @data-test-transit-input="input"
/> />
</div> </div>
@ -140,12 +140,7 @@
{{#if (or (and @verification (eq @verification "HMAC")) @hmac)}} {{#if (or (and @verification (eq @verification "HMAC")) @hmac)}}
<div class="field is-flex-column is-flex-1"> <div class="field is-flex-column is-flex-1">
<div class="control is-flex-column is-flex-1"> <div class="control is-flex-column is-flex-1">
<JsonEditor <JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @mode="ruby" />
@title="HMAC"
@value={{@hmac}}
@valueUpdated={{action (mut @hmac)}}
@options={{hash mode="ruby"}}
/>
</div> </div>
</div> </div>
{{else}} {{else}}
@ -155,7 +150,7 @@
@title="Signature" @title="Signature"
@value={{@signature}} @value={{@signature}}
@valueUpdated={{action (mut @signature)}} @valueUpdated={{action (mut @signature)}}
@options={{hash mode="ruby"}} @mode="ruby"
/> />
</div> </div>
</div> </div>
@ -165,7 +160,7 @@
{{else}} {{else}}
<div class="field"> <div class="field">
<div class="control"> <div class="control">
<JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @options={{hash mode="ruby"}} /> <JsonEditor @title="HMAC" @value={{@hmac}} @valueUpdated={{action (mut @hmac)}} @mode="ruby" />
</div> </div>
</div> </div>
<div class="field"> <div class="field">

View File

@ -62,7 +62,8 @@
@showToolbar={{false}} @showToolbar={{false}}
@value={{this.model.policy}} @value={{this.model.policy}}
@valueUpdated={{action (mut this.model.policy)}} @valueUpdated={{action (mut this.model.policy)}}
@options={{hash mode="ruby" extraKeys=(hash Shift-Enter=(action "savePolicy" this.model))}} @mode="ruby"
@extraKeys={{hash Shift-Enter=(action "savePolicy" this.model)}}
/> />
{{/if}} {{/if}}
</div> </div>

View File

@ -50,7 +50,8 @@
@title="Policy" @title="Policy"
@value={{this.model.policy}} @value={{this.model.policy}}
@valueUpdated={{action (mut this.model.policy)}} @valueUpdated={{action (mut this.model.policy)}}
@options={{hash mode="ruby" extraKeys=(hash Shift-Enter=(action "savePolicy" this.model))}} @mode="ruby"
@extraKeys={{hash Shift-Enter=(action "savePolicy" this.model)}}
/> />
<div class="box is-shadowless is-fullwidth has-short-padding"> <div class="box is-shadowless is-fullwidth has-short-padding">
<p class="help-text has-text-grey-dark is-size-7"> <p class="help-text has-text-grey-dark is-size-7">

View File

@ -42,7 +42,8 @@
@title="Policy" @title="Policy"
@subTitle={{if (eq this.policyType "acl") (concat this.uppercase this.model.format " format")}} @subTitle={{if (eq this.policyType "acl") (concat this.uppercase this.model.format " format")}}
@value={{this.model.policy}} @value={{this.model.policy}}
@options={{hash readOnly=true mode="ruby"}} @readOnly={{true}}
@mode="ruby"
/> />
</div> </div>
{{#if this.model.paths}} {{#if this.model.paths}}

View File

@ -26,10 +26,6 @@ const appConfig = {
return `${config.rootURL.replace(/\/$/, '')}${filePath}`; return `${config.rootURL.replace(/\/$/, '')}${filePath}`;
}, },
}, },
codemirror: {
modes: ['javascript', 'ruby'],
keyMaps: ['sublime'],
},
babel: { babel: {
plugins: ['@babel/plugin-proposal-object-rest-spread', ['inline-json-import', {}]], plugins: ['@babel/plugin-proposal-object-rest-spread', ['inline-json-import', {}]],
}, },
@ -74,8 +70,7 @@ module.exports = function (defaults) {
app.import('node_modules/jsonlint/lib/jsonlint.js'); app.import('node_modules/jsonlint/lib/jsonlint.js');
app.import('node_modules/codemirror/addon/lint/lint.css'); app.import('node_modules/codemirror/addon/lint/lint.css');
app.import('node_modules/codemirror/addon/lint/lint.js'); app.import('node_modules/codemirror/lib/codemirror.css');
app.import('node_modules/codemirror/addon/lint/json-lint.js');
app.import('node_modules/text-encoder-lite/text-encoder-lite.js'); app.import('node_modules/text-encoder-lite/text-encoder-lite.js');
app.import('node_modules/jsondiffpatch/dist/jsondiffpatch.umd.js'); app.import('node_modules/jsondiffpatch/dist/jsondiffpatch.umd.js');
app.import('node_modules/jsondiffpatch/dist/formatters-styles/html.css'); app.import('node_modules/jsondiffpatch/dist/formatters-styles/html.css');

View File

@ -233,7 +233,7 @@
this.attr.options.defaultValue this.attr.options.defaultValue
}} }}
@valueUpdated={{action "codemirrorUpdated" this.attr.name "string"}} @valueUpdated={{action "codemirrorUpdated" this.attr.name "string"}}
@options={{hash theme=(or this.attr.options.theme "hashi")}} @theme={{or this.attr.options.theme "hashi"}}
@helpText={{this.attr.options.helpText}} @helpText={{this.attr.options.helpText}}
> >
{{#if this.attr.options.allowReset}} {{#if this.attr.options.allowReset}}

View File

@ -129,6 +129,7 @@
"ember-maybe-import-regenerator": "^0.1.6", "ember-maybe-import-regenerator": "^0.1.6",
"ember-maybe-in-element": "^2.0.3", "ember-maybe-in-element": "^2.0.3",
"ember-modal-dialog": "^3.0.3", "ember-modal-dialog": "^3.0.3",
"ember-modifier": "^3.1.0",
"ember-page-title": "^6.0.3", "ember-page-title": "^6.0.3",
"ember-power-select": "^5.0.3", "ember-power-select": "^5.0.3",
"ember-promise-helpers": "^1.0.9", "ember-promise-helpers": "^1.0.9",

View File

@ -374,13 +374,15 @@ module('Acceptance | secrets/secret/create', function (hooks) {
await listPage.visitRoot({ backend: 'secret' }); await listPage.visitRoot({ backend: 'secret' });
await listPage.create(); await listPage.create();
await editPage.path(secretPath).toggleJSON(); await editPage.path(secretPath).toggleJSON();
await editPage.editor.fillIn(this, content); let instance = document.querySelector('.CodeMirror').CodeMirror;
instance.setValue(content);
await editPage.save(); await editPage.save();
assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.show', 'redirects to the show page'); assert.equal(currentRouteName(), 'vault.cluster.secrets.backend.show', 'redirects to the show page');
assert.ok(showPage.editIsPresent, 'shows the edit button'); assert.ok(showPage.editIsPresent, 'shows the edit button');
let savedInstance = document.querySelector('.CodeMirror').CodeMirror;
assert.equal( assert.equal(
showPage.editor.content(this), savedInstance.options.value,
JSON.stringify({ bar: 'boo', foo: 'fa' }, null, 2), JSON.stringify({ bar: 'boo', foo: 'fa' }, null, 2),
'saves the content' 'saves the content'
); );

View File

@ -19,8 +19,7 @@ module('Integration | Component | console/log json', function (hooks) {
this.set('content', objectContent); this.set('content', objectContent);
await render(hbs`{{console/log-json content=content}}`); await render(hbs`{{console/log-json content=content}}`);
const instance = this.codeMirror.instanceFor(find('[data-test-component=json-editor]').id); const instance = find('[data-test-component=code-mirror-modifier]').innerText;
assert.equal(instance, expectedText);
assert.equal(instance.getValue(), expectedText);
}); });
}); });

View File

@ -1,44 +1,76 @@
import { module, test } from 'qunit'; import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit'; import { setupRenderingTest } from 'ember-qunit';
import { create } from 'ember-cli-page-object'; import { create } from 'ember-cli-page-object';
import { render } from '@ember/test-helpers'; import { render, fillIn, find, waitUntil } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile'; import hbs from 'htmlbars-inline-precompile';
import jsonEditor from '../../pages/components/json-editor'; import jsonEditor from '../../pages/components/json-editor';
import sinon from 'sinon';
const component = create(jsonEditor); const component = create(jsonEditor);
module('Integration | Component | json-editor', function (hooks) { module('Integration | Component | json-editor', function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
const setup = async function (context, title, value, options, showToolbar = true) { const JSON_BLOB = `{
context.set('value', JSON.stringify(value)); "test": "test"
context.set('options', options); }`;
context.set('title', title); const BAD_JSON_BLOB = `{
context.set('showToolbar', showToolbar); "test": test
await render(hbs`{{json-editor title=title value=value options=options showToolbar=showToolbar}}`); }`;
};
hooks.beforeEach(function () {
this.set('valueUpdated', sinon.spy());
this.set('onFocusOut', sinon.spy());
this.set('json_blob', JSON_BLOB);
this.set('bad_json_blob', BAD_JSON_BLOB);
this.set('hashi-read-only-theme', 'hashi-read-only auto-height');
});
test('it renders', async function (assert) { test('it renders', async function (assert) {
let value = ''; await render(hbs`<JsonEditor
await setup(this, 'Test title', value, null); @value={{"{}"}}
@title={{"Test title"}}
@showToolbar={{true}}
@readOnly={{true}}
/>`);
assert.equal(component.title, 'Test title', 'renders the provided title'); assert.equal(component.title, 'Test title', 'renders the provided title');
assert.equal(component.hasToolbar, true, 'renders the toolbar'); assert.equal(component.hasToolbar, true, 'renders the toolbar');
assert.equal(component.hasJSONEditor, true, 'renders the ivy code mirror component'); assert.equal(component.hasJSONEditor, true, 'renders the code mirror modifier');
assert.equal(component.canEdit, true, 'json editor is in read only mode'); assert.ok(component.canEdit, 'json editor can be edited');
}); });
test('it renders in read only mode', async function (assert) { test('it handles editing and linting and styles to json', async function (assert) {
let value = ''; await render(hbs`<JsonEditor
let options = { @value={{this.json_blob}}
readOnly: true, @readOnly={{false}}
}; @valueUpdated={{this.valueUpdated}}
await setup(this, 'Test title', value, options); @onFocusOut={{this.onFocusOut}}
assert.equal(component.canEdit, false, 'editor is in read only mode'); />`);
// check for json styling
assert.dom('.cm-property').hasStyle({
color: 'rgb(158, 132, 197)',
});
assert.dom('.cm-string:nth-child(2)').hasStyle({
color: 'rgb(29, 219, 163)',
});
await fillIn('textarea', this.bad_json_blob);
await waitUntil(() => find('.CodeMirror-lint-marker-error'));
assert.dom('.CodeMirror-lint-marker-error').exists('throws linting error');
assert.dom('.CodeMirror-linenumber').exists('shows line numbers');
}); });
test('it renders the editor without toolbar', async function (assert) { test('it renders the correct theme and expected styling', async function (assert) {
let value = ''; await render(hbs`<JsonEditor
await setup(this, 'Test title', value, null, false); @value={{this.json_blob}}
assert.equal(component.hasToolbar, false, 'toolbar is not rendered'); @theme={{this.hashi-read-only-theme}}
@readOnly={{true}}
/>`);
assert.dom('.cm-s-hashi-read-only').hasStyle({
background: 'rgb(247, 248, 250) none repeat scroll 0% 0% / auto padding-box border-box',
});
assert.dom('.CodeMirror-linenumber').doesNotExist('on readOnly does not show line numbers');
}); });
}); });

View File

@ -1,6 +1,6 @@
import { module, test } from 'qunit'; import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit'; import { setupRenderingTest } from 'ember-qunit';
import { render, find, settled } from '@ember/test-helpers'; import { render, settled } from '@ember/test-helpers';
import { resolve } from 'rsvp'; import { resolve } from 'rsvp';
import { run } from '@ember/runloop'; import { run } from '@ember/runloop';
import Service from '@ember/service'; import Service from '@ember/service';
@ -55,15 +55,12 @@ module('Integration | Component | secret edit', function (hooks) {
test('it shows an error when creating and data is not an object', async function (assert) { test('it shows an error when creating and data is not an object', async function (assert) {
this.set('mode', 'create'); this.set('mode', 'create');
this.set('model', { this.set('model', {
secretData: { secretData: null,
int: '2',
null: 'null',
float: '1.234',
},
}); });
await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`); await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`);
let instance = this.codeMirror.instanceFor(find('[data-test-component=json-editor]').id);
let instance = document.querySelector('.CodeMirror').CodeMirror;
instance.setValue(JSON.stringify([{ foo: 'bar' }])); instance.setValue(JSON.stringify([{ foo: 'bar' }]));
await settled(); await settled();
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object'); assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');
@ -99,7 +96,7 @@ module('Integration | Component | secret edit', function (hooks) {
await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`); await render(hbs`{{secret-edit mode=mode model=model preferAdvancedEdit=true }}`);
let instance = this.codeMirror.instanceFor(find('[data-test-component=json-editor]').id); let instance = document.querySelector('.CodeMirror').CodeMirror;
instance.setValue(JSON.stringify([{ foo: 'bar' }])); instance.setValue(JSON.stringify([{ foo: 'bar' }]));
await settled(); await settled();
assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object'); assert.dom('[data-test-error]').includesText('Vault expects data to be formatted as an JSON object');

View File

@ -15,7 +15,7 @@ export default {
hasSearchSelect: isPresent('[data-test-component=search-select]'), hasSearchSelect: isPresent('[data-test-component=search-select]'),
hasTextFile: isPresent('[data-test-component=text-file]'), hasTextFile: isPresent('[data-test-component=text-file]'),
hasTTLPicker: isPresent('[data-test-toggle-input="Foo"]'), hasTTLPicker: isPresent('[data-test-toggle-input="Foo"]'),
hasJSONEditor: isPresent('[data-test-component=json-editor]'), hasJSONEditor: isPresent('[data-test-component="code-mirror-modifier"]'),
hasJSONClearButton: isPresent('[data-test-json-clear-button]'), hasJSONClearButton: isPresent('[data-test-json-clear-button]'),
hasSelect: isPresent('select'), hasSelect: isPresent('select'),
hasInput: isPresent('input'), hasInput: isPresent('input'),

View File

@ -1,8 +1,8 @@
import { isPresent, isVisible, text } from 'ember-cli-page-object'; import { isPresent, notHasClass, text } from 'ember-cli-page-object';
export default { export default {
title: text('[data-test-component=json-editor-title]'), title: text('[data-test-component=json-editor-title]'),
hasToolbar: isPresent('[data-test-component=json-editor-toolbar]'), hasToolbar: isPresent('[data-test-component=json-editor-toolbar]'),
hasJSONEditor: isPresent('[data-test-component=json-editor]'), hasJSONEditor: isPresent('[data-test-component="code-mirror-modifier"]'),
canEdit: isVisible('div.CodeMirror-gutters'), canEdit: notHasClass('readonly-codemirror'),
}; };

View File

@ -1,34 +0,0 @@
import getCodeMirrorInstance from 'vault/tests/helpers/codemirror';
// Like fillable, but for the CodeMirror editor
//
// Usage: fillIn: codeFillable('[data-test-editor]')
// Page.fillIn(code);
export function codeFillable(selector) {
return {
isDescriptor: true,
get() {
return function (context, code) {
const cm = getCodeMirrorInstance(context, selector);
cm.setValue(code);
return this;
};
},
};
}
// Like text, but for the CodeMirror editor
//
// Usage: content: code('[data-test-editor]')
// Page.code(); // some = [ 'string', 'of', 'code' ]
export function code(selector) {
return {
isDescriptor: true,
get() {
return function (context) {
const cm = getCodeMirrorInstance(context, selector);
return cm.getValue();
};
},
};
}

View File

@ -1,6 +1,6 @@
import { Base } from '../create'; import { Base } from '../create';
import { isPresent, clickable, visitable, create, fillable } from 'ember-cli-page-object'; import { isPresent, clickable, visitable, create, fillable } from 'ember-cli-page-object';
import { codeFillable } from 'vault/tests/pages/helpers/codemirror';
export default create({ export default create({
...Base, ...Base,
path: fillable('[data-test-secret-path="true"]'), path: fillable('[data-test-secret-path="true"]'),
@ -17,9 +17,6 @@ export default create({
hasMetadataFields: isPresent('[data-test-metadata-fields]'), hasMetadataFields: isPresent('[data-test-metadata-fields]'),
maxVersion: fillable('[data-test-input="maxVersions"]'), maxVersion: fillable('[data-test-input="maxVersions"]'),
startCreateSecret: clickable('[data-test-secret-create]'), startCreateSecret: clickable('[data-test-secret-create]'),
editor: {
fillIn: codeFillable('[data-test-component="json-editor"]'),
},
deleteSecret() { deleteSecret() {
return this.deleteBtn().confirmBtn(); return this.deleteBtn().confirmBtn();
}, },

View File

@ -1,6 +1,5 @@
import { Base } from '../show'; import { Base } from '../show';
import { create, clickable, collection, isPresent, text } from 'ember-cli-page-object'; import { create, clickable, collection, isPresent, text } from 'ember-cli-page-object';
import { code } from 'vault/tests/pages/helpers/codemirror';
export default create({ export default create({
...Base, ...Base,
@ -18,9 +17,7 @@ export default create({
editIsPresent: isPresent('[data-test-secret-edit]'), editIsPresent: isPresent('[data-test-secret-edit]'),
noReadIsPresent: isPresent('[data-test-write-without-read-empty-message]'), noReadIsPresent: isPresent('[data-test-write-without-read-empty-message]'),
noReadMessage: text('data-test-empty-state-message'), noReadMessage: text('data-test-empty-state-message'),
editor: {
content: code('[data-test-component="json-editor"]'),
},
deleteSecret() { deleteSecret() {
return this.deleteBtn().confirmBtn(); return this.deleteBtn().confirmBtn();
}, },

View File

@ -7974,7 +7974,12 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
codemirror@^5.47.0, codemirror@^5.58.2: codemirror@^5.47.0:
version "5.65.2"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.2.tgz#5799a70cb3d706e10f60e267245e3a75205d3dd9"
integrity sha512-SZM4Zq7XEC8Fhroqe3LxbEEX1zUPWH1wMr5zxiBuiUF64iYOUH/JI88v4tBag8MiBS8B8gRv8O1pPXGYXQ4ErA==
codemirror@^5.58.2:
version "5.61.0" version "5.61.0"
resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.61.0.tgz#318e5b034a707207948b92ffc2862195e8fdb08e" resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.61.0.tgz#318e5b034a707207948b92ffc2862195e8fdb08e"
integrity sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg== integrity sha512-D3wYH90tYY1BsKlUe0oNj2JAhQ9TepkD51auk3N7q+4uz7A/cgJ5JsWHreT0PqieW1QhOuqxQ2reCXV1YXzecg==
@ -10222,7 +10227,7 @@ ember-cli-typescript@^3.0.0, ember-cli-typescript@^3.1.3, ember-cli-typescript@^
stagehand "^1.0.0" stagehand "^1.0.0"
walk-sync "^2.0.0" walk-sync "^2.0.0"
ember-cli-typescript@^4.1.0, ember-cli-typescript@^4.2.0: ember-cli-typescript@^4.1.0, ember-cli-typescript@^4.2.0, ember-cli-typescript@^4.2.1:
version "4.2.1" version "4.2.1"
resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-4.2.1.tgz#54d08fc90318cc986f3ea562f93ce58a6cc4c24d" resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-4.2.1.tgz#54d08fc90318cc986f3ea562f93ce58a6cc4c24d"
integrity sha512-0iKTZ+/wH6UB/VTWKvGuXlmwiE8HSIGcxHamwNhEC5x1mN3z8RfvsFZdQWYUzIWFN2Tek0gmepGRPTwWdBYl/A== integrity sha512-0iKTZ+/wH6UB/VTWKvGuXlmwiE8HSIGcxHamwNhEC5x1mN3z8RfvsFZdQWYUzIWFN2Tek0gmepGRPTwWdBYl/A==
@ -10396,6 +10401,17 @@ ember-compatibility-helpers@^1.2.0, ember-compatibility-helpers@^1.2.1, ember-co
fs-extra "^9.1.0" fs-extra "^9.1.0"
semver "^5.4.1" semver "^5.4.1"
ember-compatibility-helpers@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/ember-compatibility-helpers/-/ember-compatibility-helpers-1.2.6.tgz#603579ab2fb14be567ef944da3fc2d355f779cd8"
integrity sha512-2UBUa5SAuPg8/kRVaiOfTwlXdeVweal1zdNPibwItrhR0IvPrXpaqwJDlEZnWKEoB+h33V0JIfiWleSG6hGkkA==
dependencies:
babel-plugin-debug-macros "^0.2.0"
ember-cli-version-checker "^5.1.1"
find-up "^5.0.0"
fs-extra "^9.1.0"
semver "^5.4.1"
ember-composable-helpers@^4.3.0: ember-composable-helpers@^4.3.0:
version "4.5.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/ember-composable-helpers/-/ember-composable-helpers-4.5.0.tgz#94febbdf4348e64f45f7a6f993f326e32540a61e" resolved "https://registry.yarnpkg.com/ember-composable-helpers/-/ember-composable-helpers-4.5.0.tgz#94febbdf4348e64f45f7a6f993f326e32540a61e"
@ -10656,6 +10672,17 @@ ember-modifier@^2.1.0:
ember-destroyable-polyfill "^2.0.2" ember-destroyable-polyfill "^2.0.2"
ember-modifier-manager-polyfill "^1.2.0" ember-modifier-manager-polyfill "^1.2.0"
ember-modifier@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/ember-modifier/-/ember-modifier-3.1.0.tgz#ba5b0941302accd787ed3dcfc8d20400b77ffc41"
integrity sha512-G5Lj9jVFsD2sVJcRNQfaGKG1p81wT4LGfClBhCuB4TgwP1NGJKdqI+Q8BW2MptONxQt/71UjjUH0YK7Gm9eahg==
dependencies:
ember-cli-babel "^7.26.6"
ember-cli-normalize-entity-name "^1.0.0"
ember-cli-string-utils "^1.1.0"
ember-cli-typescript "^4.2.1"
ember-compatibility-helpers "^1.2.5"
ember-native-dom-helpers@^0.7.0: ember-native-dom-helpers@^0.7.0:
version "0.7.0" version "0.7.0"
resolved "https://registry.yarnpkg.com/ember-native-dom-helpers/-/ember-native-dom-helpers-0.7.0.tgz#98a87c11a391cec5c12382a4857e59ea2fb4b00a" resolved "https://registry.yarnpkg.com/ember-native-dom-helpers/-/ember-native-dom-helpers-0.7.0.tgz#98a87c11a391cec5c12382a4857e59ea2fb4b00a"