mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 11:08:10 +00:00
ui: refactor text file component (#18458)
* wip tests * Move text-file to addon * rename fileName to filename, initial cleanup of text-fil * rename args, rename test selector * fix eye-con, remove enterAsText from file object * add tests * move files back to original location * rename files via git for git diff * adjsut test * Revert "wip tests" This reverts commit 63716a1e647a0b01236d34322837456ef3e9db43. * fix policy form input * cleanup conditional * add bottom margin * add element id * change arg name * add text area input test * add upload test to policy form Co-authored-by: Chelsea Shaw <cshaw@hashicorp.com>
This commit is contained in:
@@ -23,7 +23,7 @@ export default Component.extend({
|
|||||||
fileHelpText: null,
|
fileHelpText: null,
|
||||||
|
|
||||||
file: null,
|
file: null,
|
||||||
fileName: null,
|
filename: null,
|
||||||
fileSize: null,
|
fileSize: null,
|
||||||
fileLastModified: null,
|
fileLastModified: null,
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ export default Component.extend({
|
|||||||
const { name, size, lastModifiedDate } = fileMeta || {};
|
const { name, size, lastModifiedDate } = fileMeta || {};
|
||||||
const fileSize = size ? filesize(size) : null;
|
const fileSize = size ? filesize(size) : null;
|
||||||
this.set('file', fileAsBytes);
|
this.set('file', fileAsBytes);
|
||||||
this.set('fileName', name);
|
this.set('filename', name);
|
||||||
this.set('fileSize', fileSize);
|
this.set('fileSize', fileSize);
|
||||||
this.set('fileLastModified', lastModifiedDate);
|
this.set('fileLastModified', lastModifiedDate);
|
||||||
this.onChange(fileAsBytes, name);
|
this.onChange(fileAsBytes, name);
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default Component.extend({
|
|||||||
// If after decoding it's not b64, we want
|
// If after decoding it's not b64, we want
|
||||||
// the original as it was only encoded when we used `readAsDataURL`.
|
// the original as it was only encoded when we used `readAsDataURL`.
|
||||||
const fileData = decoded.match(BASE_64_REGEX) ? decoded : b64File;
|
const fileData = decoded.match(BASE_64_REGEX) ? decoded : b64File;
|
||||||
yield this.onChange(this.index, { value: fileData, fileName: filename });
|
yield this.onChange(this.index, { value: fileData, filename: filename });
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
</ToolbarActions>
|
</ToolbarActions>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
{{#if this.showFileUpload}}
|
{{#if this.showFileUpload}}
|
||||||
<TextFile @inputOnly={{true}} @file={{this.file}} @onChange={{this.setPolicyFromFile}} />
|
<TextFile @uploadOnly={{true}} @onChange={{this.setPolicyFromFile}} />
|
||||||
{{else}}
|
{{else}}
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
@title="Policy"
|
@title="Policy"
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export default class PolicyFormComponent extends Component {
|
|||||||
@service flashMessages;
|
@service flashMessages;
|
||||||
|
|
||||||
@tracked errorBanner = '';
|
@tracked errorBanner = '';
|
||||||
@tracked file = null;
|
|
||||||
@tracked showFileUpload = false;
|
@tracked showFileUpload = false;
|
||||||
|
|
||||||
@task
|
@task
|
||||||
@@ -50,11 +49,11 @@ export default class PolicyFormComponent extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
setPolicyFromFile(index, fileInfo) {
|
setPolicyFromFile(fileInfo) {
|
||||||
const { value, fileName } = fileInfo;
|
const { value, filename } = fileInfo;
|
||||||
this.args.model.policy = value;
|
this.args.model.policy = value;
|
||||||
if (!this.args.model.name) {
|
if (!this.args.model.name) {
|
||||||
const trimmedFileName = trimRight(fileName, ['.json', '.txt', '.hcl', '.policy']);
|
const trimmedFileName = trimRight(filename, ['.json', '.txt', '.hcl', '.policy']);
|
||||||
this.args.model.name = trimmedFileName.toLowerCase();
|
this.args.model.name = trimmedFileName.toLowerCase();
|
||||||
}
|
}
|
||||||
this.showFileUpload = false;
|
this.showFileUpload = false;
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
import Component from '@glimmer/component';
|
|
||||||
import { set, action } from '@ember/object';
|
|
||||||
import { tracked } from '@glimmer/tracking';
|
|
||||||
import { guidFor } from '@ember/object/internals';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module TextFile
|
|
||||||
* `TextFile` components are file upload components where you can either toggle to upload a file or enter text.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* <TextFile
|
|
||||||
* @inputOnly={{true}}
|
|
||||||
* @helpText="help text"
|
|
||||||
* @file={{object}}
|
|
||||||
* @onChange={{action "someOnChangeFunction"}}
|
|
||||||
* @label={{"string"}}
|
|
||||||
* />
|
|
||||||
*
|
|
||||||
* @param [inputOnly] {bool} - When true, only the file input will be rendered
|
|
||||||
* @param [helpText] {string} - Text underneath label.
|
|
||||||
* @param file {object} - * Object in the shape of:
|
|
||||||
* {
|
|
||||||
* value: 'file contents here',
|
|
||||||
* fileName: 'nameOfFile.txt',
|
|
||||||
* enterAsText: boolean ability to enter as text
|
|
||||||
* }
|
|
||||||
* @param [onChange=Function.prototype] {Function|action} - A function to call when the value of the input changes.
|
|
||||||
* @param [label=null] {string} - Text to use as the label for the file input. If null, a default will be rendered.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default class TextFile extends Component {
|
|
||||||
fileHelpText = 'Select a file from your computer';
|
|
||||||
textareaHelpText = 'Enter the value as text';
|
|
||||||
elementId = guidFor(this);
|
|
||||||
index = '';
|
|
||||||
|
|
||||||
@tracked file = null;
|
|
||||||
@tracked showValue = false;
|
|
||||||
|
|
||||||
get inputOnly() {
|
|
||||||
return this.args.inputOnly || false;
|
|
||||||
}
|
|
||||||
get label() {
|
|
||||||
return this.args.label || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
readFile(file) {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = () => this.setFile(reader.result, file.name);
|
|
||||||
reader.readAsText(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
setFile(contents, filename) {
|
|
||||||
this.args.onChange(this.index, { value: contents, fileName: filename });
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
pickedFile(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const { files } = e.target;
|
|
||||||
if (!files.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (let i = 0, len = files.length; i < len; i++) {
|
|
||||||
this.readFile(files[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@action
|
|
||||||
updateData(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const file = this.args.file;
|
|
||||||
set(file, 'value', e.target.value);
|
|
||||||
this.args.onChange(this.index, file);
|
|
||||||
}
|
|
||||||
@action
|
|
||||||
clearFile() {
|
|
||||||
this.args.onChange(this.index, { value: '' });
|
|
||||||
}
|
|
||||||
@action
|
|
||||||
toggleMask() {
|
|
||||||
this.showValue = !this.showValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,9 +13,9 @@
|
|||||||
Choose a file…
|
Choose a file…
|
||||||
</label>
|
</label>
|
||||||
<span class="file-name has-text-grey-dark" data-test-text-file-input-label={{true}}>
|
<span class="file-name has-text-grey-dark" data-test-text-file-input-label={{true}}>
|
||||||
{{or this.fileName "No file chosen"}}
|
{{or this.filename "No file chosen"}}
|
||||||
</span>
|
</span>
|
||||||
{{#if this.fileName}}
|
{{#if this.filename}}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="file-delete-button"
|
class="file-delete-button"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if this.fileName}}
|
{{#if this.filename}}
|
||||||
<p class="help has-text-grey">
|
<p class="help has-text-grey">
|
||||||
This file is
|
This file is
|
||||||
{{this.fileSize}}
|
{{this.fileSize}}
|
||||||
|
|||||||
@@ -58,13 +58,13 @@
|
|||||||
<Icon @name="file" />
|
<Icon @name="file" />
|
||||||
</span>
|
</span>
|
||||||
<label for="file-input" class="file-label has-text-grey-dark" data-test-pgp-file-input-label={{true}}>
|
<label for="file-input" class="file-label has-text-grey-dark" data-test-pgp-file-input-label={{true}}>
|
||||||
{{#if this.key.fileName}}
|
{{#if this.key.filename}}
|
||||||
{{this.key.fileName}}
|
{{this.key.filename}}
|
||||||
{{else}}
|
{{else}}
|
||||||
Choose a file…
|
Choose a file…
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</label>
|
</label>
|
||||||
{{#if this.key.fileName}}
|
{{#if this.key.filename}}
|
||||||
<button type="button" class="file-delete-button" {{action "clearKey"}} data-test-pgp-clear={{true}}>
|
<button type="button" class="file-delete-button" {{action "clearKey"}} data-test-pgp-clear={{true}}>
|
||||||
<Icon @name="x" aria-label="Close" />
|
<Icon @name="x" aria-label="Close" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
{{#unless this.inputOnly}}
|
|
||||||
<div class="level is-mobile">
|
|
||||||
<div class="level-left">
|
|
||||||
<label class="is-label" data-test-text-label={{true}}>
|
|
||||||
{{#if this.label}}
|
|
||||||
{{this.label}}
|
|
||||||
{{#if @helpText}}
|
|
||||||
<InfoTooltip>
|
|
||||||
<span data-test-help-text>
|
|
||||||
{{@helpText}}
|
|
||||||
</span>
|
|
||||||
</InfoTooltip>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
File
|
|
||||||
{{/if}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="level-right">
|
|
||||||
<div class="control is-flex">
|
|
||||||
<input
|
|
||||||
data-test-text-toggle
|
|
||||||
id={{concat "useText-" this.elementId}}
|
|
||||||
type="checkbox"
|
|
||||||
name={{concat "useText-" this.elementId}}
|
|
||||||
class="switch is-rounded is-success is-small"
|
|
||||||
checked={{@file.enterAsText}}
|
|
||||||
{{on "change" (toggle "enterAsText" @file)}}
|
|
||||||
/>
|
|
||||||
<label for={{concat "useText-" this.elementId}}>
|
|
||||||
Enter as text
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
<div
|
|
||||||
class="field text-file box is-fullwidth is-marginless is-shadowless {{if this.inputOnly 'is-paddingless'}}"
|
|
||||||
data-test-component="text-file"
|
|
||||||
>
|
|
||||||
{{#if @file.enterAsText}}
|
|
||||||
<div class="control has-icon-right">
|
|
||||||
<textarea
|
|
||||||
class="textarea {{unless this.showValue 'masked-font'}}"
|
|
||||||
{{on "input" this.updateData}}
|
|
||||||
data-test-text-file-textarea={{true}}
|
|
||||||
>{{@file.value}}</textarea>
|
|
||||||
<button
|
|
||||||
{{on "click" this.toggleMask}}
|
|
||||||
type="button"
|
|
||||||
class="{{if (eq this.value '') 'has-text-grey'}} masked-input-toggle button {{if this.displayOnly 'is-compact'}}"
|
|
||||||
data-test-button
|
|
||||||
>
|
|
||||||
<Icon @name={{if this.showValue "eye" "eye-off"}} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<p class="help has-text-grey">
|
|
||||||
{{this.textareaHelpText}}
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<div class="control is-expanded">
|
|
||||||
<div class="file has-name is-fullwidth">
|
|
||||||
<div class="file-label" aria-label="Choose a file">
|
|
||||||
<Input
|
|
||||||
id="file-input"
|
|
||||||
class="file-input"
|
|
||||||
@type="file"
|
|
||||||
{{on "change" this.pickedFile}}
|
|
||||||
data-test-text-file-input={{true}}
|
|
||||||
/>
|
|
||||||
<label for="file-input" class="file-cta button">
|
|
||||||
<Icon @name="upload" class="has-light-grey-text" />
|
|
||||||
Choose a file…
|
|
||||||
</label>
|
|
||||||
<span class="file-name has-text-grey-dark" data-test-text-file-input-label={{true}}>
|
|
||||||
{{#if @file.fileName}}
|
|
||||||
{{@file.fileName}}
|
|
||||||
{{else}}
|
|
||||||
No file chosen
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
|
||||||
{{#if @file.fileName}}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="file-delete-button"
|
|
||||||
aria-label="Clear file selection"
|
|
||||||
{{on "click" this.clearFile}}
|
|
||||||
data-test-text-clear={{true}}
|
|
||||||
>
|
|
||||||
<Icon @name="x-circle" />
|
|
||||||
</button>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p class="help has-text-grey">
|
|
||||||
{{this.fileHelpText}}
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
@@ -125,12 +125,14 @@
|
|||||||
/>
|
/>
|
||||||
{{else if (eq @attr.options.editType "file")}}
|
{{else if (eq @attr.options.editType "file")}}
|
||||||
{{! File Input }}
|
{{! File Input }}
|
||||||
<TextFile
|
<div class="has-bottom-margin-m">
|
||||||
@helpText={{@attr.options.helpText}}
|
<TextFile
|
||||||
@file={{this.file}}
|
@label={{this.labelString}}
|
||||||
@onChange={{this.setFile}}
|
@helpText={{@attr.options.helpText}}
|
||||||
@label={{this.labelString}}
|
@onChange={{this.setFile}}
|
||||||
/>
|
@validationError={{this.validationError}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{{else if (eq @attr.options.editType "ttl")}}
|
{{else if (eq @attr.options.editType "ttl")}}
|
||||||
{{! TTL Picker }}
|
{{! TTL Picker }}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
@@ -302,7 +304,7 @@
|
|||||||
value={{or (get @model this.valuePath) @attr.options.defaultValue}}
|
value={{or (get @model this.valuePath) @attr.options.defaultValue}}
|
||||||
onchange={{this.onChangeWithEvent}}
|
onchange={{this.onChangeWithEvent}}
|
||||||
onkeyup={{this.handleKeyUp}}
|
onkeyup={{this.handleKeyUp}}
|
||||||
class="input {{if this.validationError 'has-error-border'}}"
|
class="input {{if this.validationError 'has-error-border'}} has-bottom-margin-m"
|
||||||
maxLength={{@attr.options.characterLimit}}
|
maxLength={{@attr.options.characterLimit}}
|
||||||
/>
|
/>
|
||||||
{{#if @attr.options.validationAttr}}
|
{{#if @attr.options.validationAttr}}
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ export default class FormFieldComponent extends Component {
|
|||||||
'ttl',
|
'ttl',
|
||||||
];
|
];
|
||||||
@tracked showInput = false;
|
@tracked showInput = false;
|
||||||
@tracked file = { value: '' }; // used by the pgp-file component when an attr is editType of 'file'
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
@@ -116,12 +115,11 @@ export default class FormFieldComponent extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
setFile(_, keyFile) {
|
setFile(keyFile) {
|
||||||
const path = this.valuePath;
|
const path = this.valuePath;
|
||||||
const { value } = keyFile;
|
const { value } = keyFile;
|
||||||
this.args.model.set(path, value);
|
this.args.model.set(path, value);
|
||||||
this.onChange(path, value);
|
this.onChange(path, value);
|
||||||
this.file = keyFile;
|
|
||||||
}
|
}
|
||||||
@action
|
@action
|
||||||
setAndBroadcast(value) {
|
setAndBroadcast(value) {
|
||||||
|
|||||||
95
ui/lib/core/addon/components/text-file.hbs
Normal file
95
ui/lib/core/addon/components/text-file.hbs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
{{#unless @uploadOnly}}
|
||||||
|
<div class="level is-mobile">
|
||||||
|
<div class="level-left">
|
||||||
|
<label for="input-{{this.elementId}}" class="is-label" data-test-text-file-label>
|
||||||
|
{{or @label "File"}}
|
||||||
|
{{#if @helpText}}
|
||||||
|
<InfoTooltip>
|
||||||
|
<span data-test-help-text>
|
||||||
|
{{@helpText}}
|
||||||
|
</span>
|
||||||
|
</InfoTooltip>
|
||||||
|
{{/if}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="level-right">
|
||||||
|
<div class="control is-flex">
|
||||||
|
<Input
|
||||||
|
data-test-text-toggle
|
||||||
|
id="use-text-{{this.elementId}}"
|
||||||
|
class="switch is-rounded is-success is-small"
|
||||||
|
@type="checkbox"
|
||||||
|
@checked={{this.showTextArea}}
|
||||||
|
{{on "change" (fn (mut this.showTextArea) (not this.showTextArea))}}
|
||||||
|
/>
|
||||||
|
<label for="use-text-{{this.elementId}}">
|
||||||
|
Enter as text
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
<div class="field text-file box is-fullwidth is-marginless is-shadowless is-paddingless" data-test-component="text-file">
|
||||||
|
{{#if this.showTextArea}}
|
||||||
|
<div class="control has-icon-right">
|
||||||
|
<textarea
|
||||||
|
id="input-{{this.elementId}}"
|
||||||
|
class="textarea {{if (and (not this.showValue) this.content) 'masked-font'}}"
|
||||||
|
{{on "input" this.handleTextInput}}
|
||||||
|
data-test-text-file-textarea
|
||||||
|
>
|
||||||
|
{{this.content}}
|
||||||
|
</textarea>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="masked-input-toggle button is-compact"
|
||||||
|
data-test-button="toggle-masked"
|
||||||
|
{{on "click" (fn (mut this.showValue) (not this.showValue))}}
|
||||||
|
>
|
||||||
|
<Icon @name={{if this.showValue "eye" "eye-off"}} @stretched={{true}} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-grey">Enter the value as text</p>
|
||||||
|
{{else}}
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<div class="file has-name is-fullwidth">
|
||||||
|
<div class="file-label" aria-label="Choose a file">
|
||||||
|
<Input
|
||||||
|
id="file-input-{{this.elementId}}"
|
||||||
|
class="file-input"
|
||||||
|
@type="file"
|
||||||
|
{{on "change" this.handleFileUpload}}
|
||||||
|
data-test-text-file-input
|
||||||
|
/>
|
||||||
|
<label for="file-input-{{this.elementId}}" class="file-cta button">
|
||||||
|
<Icon @name="upload" class="has-light-grey-text" />
|
||||||
|
Choose a file…
|
||||||
|
</label>
|
||||||
|
<span class="file-name has-text-grey-dark" data-test-text-file-input-label>
|
||||||
|
{{or this.filename "No file chosen"}}
|
||||||
|
</span>
|
||||||
|
{{#if this.filename}}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="file-delete-button"
|
||||||
|
aria-label="Clear file selection"
|
||||||
|
{{on "click" this.clearFile}}
|
||||||
|
data-test-text-clear
|
||||||
|
>
|
||||||
|
<Icon @name="x-circle" />
|
||||||
|
</button>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help has-text-grey">Select a file from your computer</p>
|
||||||
|
{{#if (or @validationError this.uploadError)}}
|
||||||
|
<AlertInline
|
||||||
|
@type="danger"
|
||||||
|
@message={{or @validationError this.uploadError}}
|
||||||
|
@paddingTop={{true}}
|
||||||
|
data-test-field-validation="text-file"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
69
ui/lib/core/addon/components/text-file.js
Normal file
69
ui/lib/core/addon/components/text-file.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import Component from '@glimmer/component';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
import { guidFor } from '@ember/object/internals';
|
||||||
|
/**
|
||||||
|
* @module TextFile
|
||||||
|
* `TextFile` components render a file upload input with the option to toggle a "Enter as text" button
|
||||||
|
* that changes the input into a textarea
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* <TextFile
|
||||||
|
* @uploadOnly={{true}}
|
||||||
|
* @helpText="help text"
|
||||||
|
* @onChange={{this.handleChange}}
|
||||||
|
* @label="PEM Bundle"
|
||||||
|
* />
|
||||||
|
*
|
||||||
|
* @param {function} onChange - Callback function to call when the value of the input changes, returns an object in the shape of { value: fileContents, filename: 'some-file.txt' }
|
||||||
|
* @param {bool} [uploadOnly=false] - When true, renders a static file upload input and removes the option to toggle and input plain text
|
||||||
|
* @param {string} [helpText] - Text underneath label.
|
||||||
|
* @param {string} [label='File'] - Text to use as the label for the file input. If none, default of 'File' is rendered
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class TextFileComponent extends Component {
|
||||||
|
@tracked content = '';
|
||||||
|
@tracked filename = '';
|
||||||
|
@tracked uploadError = '';
|
||||||
|
@tracked showValue = false;
|
||||||
|
@tracked showTextArea = false;
|
||||||
|
elementId = guidFor(this);
|
||||||
|
|
||||||
|
async readFile(file) {
|
||||||
|
try {
|
||||||
|
this.content = await file.text();
|
||||||
|
this.filename = file.name;
|
||||||
|
this.handleChange();
|
||||||
|
} catch (error) {
|
||||||
|
this.clearFile();
|
||||||
|
this.uploadError = 'There was a problem uploading. Please try again.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
handleFileUpload(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const { files } = e.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
this.readFile(files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
handleTextInput(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.content = e.target.value;
|
||||||
|
this.handleChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
clearFile() {
|
||||||
|
this.content = '';
|
||||||
|
this.filename = '';
|
||||||
|
this.handleChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange() {
|
||||||
|
this.args.onChange({ value: this.content, filename: this.filename });
|
||||||
|
this.uploadError = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{{#if this.isFlightIcon}}
|
{{#if this.isFlightIcon}}
|
||||||
<FlightIcon @name={{@name}} @size={{@size}} ...attributes />
|
<FlightIcon @name={{@name}} @size={{@size}} @stretched={{@stretched}} ...attributes />
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="hs-icon {{this.hsIconClass}}" ...attributes>
|
<span class="hs-icon {{this.hsIconClass}}" ...attributes>
|
||||||
{{svg-jar @name}}
|
{{svg-jar @name}}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@
|
|||||||
onclick={{action "toggleMask"}}
|
onclick={{action "toggleMask"}}
|
||||||
type="button"
|
type="button"
|
||||||
class="{{if (eq this.value '') 'has-text-grey'}} masked-input-toggle button"
|
class="{{if (eq this.value '') 'has-text-grey'}} masked-input-toggle button"
|
||||||
data-test-button
|
data-test-button="toggle-masked"
|
||||||
>
|
>
|
||||||
<Icon @name={{if this.showValue "eye" "eye-off"}} />
|
<Icon @name={{if this.showValue "eye" "eye-off"}} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -107,7 +107,7 @@
|
|||||||
<div class="message-body">
|
<div class="message-body">
|
||||||
<h4 class="title is-7 is-marginless">
|
<h4 class="title is-7 is-marginless">
|
||||||
PGP Key
|
PGP Key
|
||||||
{{this.pgpKeyFile.fileName}}
|
{{this.pgpKeyFile.filename}}
|
||||||
</h4>
|
</h4>
|
||||||
<code class="is-word-break">{{this.pgp_key}}</code>
|
<code class="is-word-break">{{this.pgp_key}}</code>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -129,7 +129,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<h4 class="field-title has-bottom-padding-m is-fullwidth">
|
<h4 class="field-title has-bottom-padding-m is-fullwidth">
|
||||||
PGP Key
|
PGP Key
|
||||||
{{this.pgpKeyFile.fileName}}
|
{{this.pgpKeyFile.filename}}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="message is-list has-copy-button" tabindex="-1">
|
<div class="message is-list has-copy-button" tabindex="-1">
|
||||||
<HoverCopyButton @copyValue={{this.pgp_key}} />
|
<HoverCopyButton @copyValue={{this.pgp_key}} />
|
||||||
|
|||||||
1
ui/lib/core/app/components/text-file.js
Normal file
1
ui/lib/core/app/components/text-file.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default } from 'core/components/text-file';
|
||||||
@@ -105,7 +105,7 @@ module('Integration | Component | form field', function (hooks) {
|
|||||||
await click('[data-test-text-toggle]');
|
await click('[data-test-text-toggle]');
|
||||||
await fillIn('[data-test-text-file-textarea]', 'hello world');
|
await fillIn('[data-test-text-file-textarea]', 'hello world');
|
||||||
assert.dom('[data-test-text-file-textarea]').hasClass('masked-font');
|
assert.dom('[data-test-text-file-textarea]').hasClass('masked-font');
|
||||||
await click('[data-test-button]');
|
await click('[data-test-button="toggle-masked"]');
|
||||||
assert.dom('[data-test-text-file-textarea]').doesNotHaveClass('masked-font');
|
assert.dom('[data-test-text-file-textarea]').doesNotHaveClass('masked-font');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { setupRenderingTest } from 'ember-qunit';
|
import { setupRenderingTest } from 'ember-qunit';
|
||||||
import { click, fillIn, render } from '@ember/test-helpers';
|
import { click, fillIn, render, triggerEvent } from '@ember/test-helpers';
|
||||||
import hbs from 'htmlbars-inline-precompile';
|
import hbs from 'htmlbars-inline-precompile';
|
||||||
import Sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import Pretender from 'pretender';
|
import Pretender from 'pretender';
|
||||||
|
|
||||||
const SELECTORS = {
|
const SELECTORS = {
|
||||||
@@ -21,8 +21,8 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
hooks.beforeEach(function () {
|
hooks.beforeEach(function () {
|
||||||
this.store = this.owner.lookup('service:store');
|
this.store = this.owner.lookup('service:store');
|
||||||
this.model = this.store.createRecord('policy/acl');
|
this.model = this.store.createRecord('policy/acl');
|
||||||
this.onSave = Sinon.spy();
|
this.onSave = sinon.spy();
|
||||||
this.onCancel = Sinon.spy();
|
this.onCancel = sinon.spy();
|
||||||
this.server = new Pretender(function () {
|
this.server = new Pretender(function () {
|
||||||
this.put('/v1/sys/policies/acl/bad-policy', () => {
|
this.put('/v1/sys/policies/acl/bad-policy', () => {
|
||||||
return [
|
return [
|
||||||
@@ -44,15 +44,11 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it renders the form for new ACL policy', async function (assert) {
|
test('it renders the form for new ACL policy', async function (assert) {
|
||||||
const saveSpy = Sinon.spy();
|
|
||||||
const model = this.store.createRecord('policy/acl');
|
|
||||||
const policy = `
|
const policy = `
|
||||||
path "secret/*" {
|
path "secret/*" {
|
||||||
capabilities = [ "create", "read", "update", "list" ]
|
capabilities = [ "create", "read", "update", "list" ]
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
this.set('model', model);
|
|
||||||
this.set('onSave', saveSpy);
|
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<PolicyForm
|
<PolicyForm
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
@@ -67,14 +63,13 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
assert.strictEqual(this.model.name, 'foo', 'Input sets name on model to lowercase input');
|
assert.strictEqual(this.model.name, 'foo', 'Input sets name on model to lowercase input');
|
||||||
await fillIn(`${SELECTORS.policyEditor} textarea`, policy);
|
await fillIn(`${SELECTORS.policyEditor} textarea`, policy);
|
||||||
assert.strictEqual(this.model.policy, policy, 'Policy editor sets policy on model');
|
assert.strictEqual(this.model.policy, policy, 'Policy editor sets policy on model');
|
||||||
assert.ok(saveSpy.notCalled);
|
assert.ok(this.onSave.notCalled);
|
||||||
assert.dom(SELECTORS.saveButton).hasText('Create policy');
|
assert.dom(SELECTORS.saveButton).hasText('Create policy');
|
||||||
await click(SELECTORS.saveButton);
|
await click(SELECTORS.saveButton);
|
||||||
assert.ok(saveSpy.calledOnceWith(this.model));
|
assert.ok(this.onSave.calledOnceWith(this.model));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders the form for new RGP policy', async function (assert) {
|
test('it renders the form for new RGP policy', async function (assert) {
|
||||||
const saveSpy = Sinon.spy();
|
|
||||||
const model = this.store.createRecord('policy/rgp');
|
const model = this.store.createRecord('policy/rgp');
|
||||||
const policy = `
|
const policy = `
|
||||||
path "secret/*" {
|
path "secret/*" {
|
||||||
@@ -82,7 +77,6 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
this.set('model', model);
|
this.set('model', model);
|
||||||
this.set('onSave', saveSpy);
|
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<PolicyForm
|
<PolicyForm
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
@@ -97,13 +91,18 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
assert.strictEqual(this.model.name, 'foo', 'Input sets name on model to lowercase input');
|
assert.strictEqual(this.model.name, 'foo', 'Input sets name on model to lowercase input');
|
||||||
await fillIn(`${SELECTORS.policyEditor} textarea`, policy);
|
await fillIn(`${SELECTORS.policyEditor} textarea`, policy);
|
||||||
assert.strictEqual(this.model.policy, policy, 'Policy editor sets policy on model');
|
assert.strictEqual(this.model.policy, policy, 'Policy editor sets policy on model');
|
||||||
assert.ok(saveSpy.notCalled);
|
assert.ok(this.onSave.notCalled);
|
||||||
assert.dom(SELECTORS.saveButton).hasText('Create policy');
|
assert.dom(SELECTORS.saveButton).hasText('Create policy');
|
||||||
await click(SELECTORS.saveButton);
|
await click(SELECTORS.saveButton);
|
||||||
assert.ok(saveSpy.calledOnceWith(this.model));
|
assert.ok(this.onSave.calledOnceWith(this.model));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it toggles upload on new policy', async function (assert) {
|
test('it toggles to upload a new policy and uploads file', async function (assert) {
|
||||||
|
const policy = `
|
||||||
|
path "auth/token/lookup-self" {
|
||||||
|
capabilities = ["read"]
|
||||||
|
}`;
|
||||||
|
this.file = new File([policy], 'test-policy.hcl');
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<PolicyForm
|
<PolicyForm
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
@@ -117,10 +116,13 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
await click(SELECTORS.uploadFileToggle);
|
await click(SELECTORS.uploadFileToggle);
|
||||||
assert.dom(SELECTORS.policyUpload).exists({ count: 1 }, 'Policy upload is shown after toggle');
|
assert.dom(SELECTORS.policyUpload).exists({ count: 1 }, 'Policy upload is shown after toggle');
|
||||||
assert.dom(SELECTORS.policyEditor).doesNotExist('Policy editor is not shown');
|
assert.dom(SELECTORS.policyEditor).doesNotExist('Policy editor is not shown');
|
||||||
|
await triggerEvent(SELECTORS.policyUpload, 'change', { files: [this.file] });
|
||||||
|
assert.dom(SELECTORS.nameInput).hasValue('test-policy', 'it fills in policy name');
|
||||||
|
await click(SELECTORS.saveButton);
|
||||||
|
assert.propEqual(this.onSave.lastCall.args[0].policy, policy, 'policy content saves in correct format');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders the form to edit existing ACL policy', async function (assert) {
|
test('it renders the form to edit existing ACL policy', async function (assert) {
|
||||||
const saveSpy = Sinon.spy();
|
|
||||||
const model = this.store.createRecord('policy/acl', {
|
const model = this.store.createRecord('policy/acl', {
|
||||||
name: 'bar',
|
name: 'bar',
|
||||||
policy: 'some policy content',
|
policy: 'some policy content',
|
||||||
@@ -128,7 +130,6 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
model.save();
|
model.save();
|
||||||
|
|
||||||
this.set('model', model);
|
this.set('model', model);
|
||||||
this.set('onSave', saveSpy);
|
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<PolicyForm
|
<PolicyForm
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
@@ -145,13 +146,12 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
'updated-some policy content',
|
'updated-some policy content',
|
||||||
'Policy editor updates policy value on model'
|
'Policy editor updates policy value on model'
|
||||||
);
|
);
|
||||||
assert.ok(saveSpy.notCalled);
|
assert.ok(this.onSave.notCalled);
|
||||||
assert.dom(SELECTORS.saveButton).hasText('Save', 'Save button text is correct');
|
assert.dom(SELECTORS.saveButton).hasText('Save', 'Save button text is correct');
|
||||||
await click(SELECTORS.saveButton);
|
await click(SELECTORS.saveButton);
|
||||||
assert.ok(saveSpy.calledOnceWith(this.model));
|
assert.ok(this.onSave.calledOnceWith(this.model));
|
||||||
});
|
});
|
||||||
test('it renders the form to edit existing RGP policy', async function (assert) {
|
test('it renders the form to edit existing RGP policy', async function (assert) {
|
||||||
const saveSpy = Sinon.spy();
|
|
||||||
const model = this.store.createRecord('policy/rgp', {
|
const model = this.store.createRecord('policy/rgp', {
|
||||||
name: 'bar',
|
name: 'bar',
|
||||||
policy: 'some policy content',
|
policy: 'some policy content',
|
||||||
@@ -159,7 +159,6 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
model.save();
|
model.save();
|
||||||
|
|
||||||
this.set('model', model);
|
this.set('model', model);
|
||||||
this.set('onSave', saveSpy);
|
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<PolicyForm
|
<PolicyForm
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
@@ -176,20 +175,18 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
'updated-some policy content',
|
'updated-some policy content',
|
||||||
'Policy editor updates policy value on model'
|
'Policy editor updates policy value on model'
|
||||||
);
|
);
|
||||||
assert.ok(saveSpy.notCalled);
|
assert.ok(this.onSave.notCalled);
|
||||||
assert.dom(SELECTORS.saveButton).hasText('Save', 'Save button text is correct');
|
assert.dom(SELECTORS.saveButton).hasText('Save', 'Save button text is correct');
|
||||||
await click(SELECTORS.saveButton);
|
await click(SELECTORS.saveButton);
|
||||||
assert.ok(saveSpy.calledOnceWith(this.model));
|
assert.ok(this.onSave.calledOnceWith(this.model));
|
||||||
});
|
});
|
||||||
test('it shows the error message on form when save fails', async function (assert) {
|
test('it shows the error message on form when save fails', async function (assert) {
|
||||||
const saveSpy = Sinon.spy();
|
|
||||||
const model = this.store.createRecord('policy/acl', {
|
const model = this.store.createRecord('policy/acl', {
|
||||||
name: 'bad-policy',
|
name: 'bad-policy',
|
||||||
policy: 'some policy content',
|
policy: 'some policy content',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.set('model', model);
|
this.set('model', model);
|
||||||
this.set('onSave', saveSpy);
|
|
||||||
await render(hbs`
|
await render(hbs`
|
||||||
<PolicyForm
|
<PolicyForm
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
@@ -198,7 +195,7 @@ module('Integration | Component | policy-form', function (hooks) {
|
|||||||
/>
|
/>
|
||||||
`);
|
`);
|
||||||
await click(SELECTORS.saveButton);
|
await click(SELECTORS.saveButton);
|
||||||
assert.ok(saveSpy.notCalled);
|
assert.ok(this.onSave.notCalled);
|
||||||
assert.dom(SELECTORS.error).includesText('An error occurred');
|
assert.dom(SELECTORS.error).includesText('An error occurred');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
113
ui/tests/integration/components/text-file-test.js
Normal file
113
ui/tests/integration/components/text-file-test.js
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupRenderingTest } from 'vault/tests/helpers';
|
||||||
|
import { click, fillIn, render, triggerEvent } from '@ember/test-helpers';
|
||||||
|
import { hbs } from 'ember-cli-htmlbars';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
const SELECTORS = {
|
||||||
|
label: '[data-test-text-file-label]',
|
||||||
|
toggle: '[data-test-text-toggle]',
|
||||||
|
textarea: '[data-test-text-file-textarea]',
|
||||||
|
fileUpload: '[data-test-text-file-input]',
|
||||||
|
};
|
||||||
|
module('Integration | Component | text-file', function (hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
this.label = 'Some label';
|
||||||
|
this.onChange = sinon.spy();
|
||||||
|
this.owner.lookup('service:flash-messages').registerTypes(['danger']);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders with label and toggle by default', async function (assert) {
|
||||||
|
await render(hbs`<TextFile @onChange={{this.onChange}} />`);
|
||||||
|
|
||||||
|
assert.dom(SELECTORS.label).hasText('File', 'renders default label');
|
||||||
|
assert.dom(SELECTORS.toggle).exists({ count: 1 }, 'toggle exists');
|
||||||
|
assert.dom(SELECTORS.fileUpload).exists({ count: 1 }, 'File input shown');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders without toggle and option for text input when uploadOnly=true', async function (assert) {
|
||||||
|
await render(hbs`<TextFile @onChange={{this.onChange}} @uploadOnly={{true}} />`);
|
||||||
|
|
||||||
|
assert.dom(SELECTORS.label).doesNotExist('Label no longer rendered');
|
||||||
|
assert.dom(SELECTORS.toggle).doesNotExist('toggle no longer rendered');
|
||||||
|
assert.dom(SELECTORS.fileUpload).exists({ count: 1 }, 'File input shown');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it toggles between upload and textarea', async function (assert) {
|
||||||
|
await render(hbs`<TextFile @onChange={{this.onChange}} />`);
|
||||||
|
|
||||||
|
assert.dom(SELECTORS.fileUpload).exists({ count: 1 }, 'File input shown');
|
||||||
|
assert.dom(SELECTORS.textarea).doesNotExist('Texarea hidden');
|
||||||
|
await click(SELECTORS.toggle);
|
||||||
|
assert.dom(SELECTORS.textarea).exists({ count: 1 }, 'Textarea shown');
|
||||||
|
assert.dom(SELECTORS.fileUpload).doesNotExist('File upload hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it correctly parses uploaded files', async function (assert) {
|
||||||
|
this.file = new File(['some content for a file'], 'filename.txt');
|
||||||
|
await render(hbs`<TextFile @onChange={{this.onChange}} />`);
|
||||||
|
await triggerEvent(SELECTORS.fileUpload, 'change', { files: [this.file] });
|
||||||
|
assert.propEqual(
|
||||||
|
this.onChange.lastCall.args[0],
|
||||||
|
{
|
||||||
|
filename: 'filename.txt',
|
||||||
|
value: 'some content for a file',
|
||||||
|
},
|
||||||
|
'parent callback function is called with correct arguments'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it correctly submits text input', async function (assert) {
|
||||||
|
const PEM_BUNDLE = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDGjCCAgKgAwIBAgIUFvnhb2nQ8+KNS3SzjlfYDMHGIRgwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwDTELMAkGA1UEAxMCZmEwHhcNMTgwMTEwMTg1NDI5WhcNMTgwMjExMTg1NDU5
|
||||||
|
WjANMQswCQYDVQQDEwJmYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||||
|
AN2VtBn6EMlA4aYre/xoKHxlgNDxJnfSQWfs6yF/K201qPnt4QF9AXChatbmcKVn
|
||||||
|
OaURq+XEJrGVgF/u2lSos3NRZdhWVe8o3/sOetsGxcrd0gXAieOSmkqJjp27bYdl
|
||||||
|
uY3WsxhyiPvdfS6xz39OehsK/YCB6qCzwB4eEfSKqbkvfDL9sLlAiOlaoHC9pczf
|
||||||
|
6/FANKp35UDwInSwmq5vxGbnWk9zMkh5Jq6hjOWHZnVc2J8J49PYvkIM8uiHDgOE
|
||||||
|
w71T2xM5plz6crmZnxPCOcTKIdF7NTEP2lUfiqc9lONV9X1Pi4UclLPHJf5bwTmn
|
||||||
|
JaWgbKeY+IlF61/mgxzhC7cCAwEAAaNyMHAwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
|
||||||
|
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFLDtc6+HZN2lv60JSDAZq3+IHoq7MB8GA1Ud
|
||||||
|
IwQYMBaAFLDtc6+HZN2lv60JSDAZq3+IHoq7MA0GA1UdEQQGMASCAmZhMA0GCSqG
|
||||||
|
SIb3DQEBCwUAA4IBAQDVt6OddTV1MB0UvF5v4zL1bEB9bgXvWx35v/FdS+VGn/QP
|
||||||
|
cC2c4ZNukndyHhysUEPdqVg4+up1aXm4eKXzNmGMY/ottN2pEhVEWQyoIIA1tH0e
|
||||||
|
8Kv/bysYpHZKZuoGg5+mdlHS2p2Dh2bmYFyBLJ8vaeP83NpTs2cNHcmEvWh/D4UN
|
||||||
|
UmYDODRN4qh9xYruKJ8i89iMGQfbdcq78dCC4JwBIx3bysC8oF4lqbTYoYNVTnAi
|
||||||
|
LVqvLdHycEOMlqV0ecq8uMLhPVBalCmIlKdWNQFpXB0TQCsn95rCCdi7ZTsYk5zv
|
||||||
|
Q4raFvQrZth3Cz/X5yPTtQL78oBYrmHzoQKDFJ2z
|
||||||
|
-----END CERTIFICATE-----`;
|
||||||
|
|
||||||
|
await render(hbs`<TextFile @onChange={{this.onChange}} />`);
|
||||||
|
await click(SELECTORS.toggle);
|
||||||
|
await fillIn(SELECTORS.textarea, PEM_BUNDLE);
|
||||||
|
assert.propEqual(
|
||||||
|
this.onChange.lastCall.args[0],
|
||||||
|
{
|
||||||
|
filename: '',
|
||||||
|
value: PEM_BUNDLE,
|
||||||
|
},
|
||||||
|
'parent callback function is called with correct text area input'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it throws an error when it cannot read the file', async function (assert) {
|
||||||
|
this.file = { foo: 'bar' };
|
||||||
|
await render(hbs`<TextFile @onChange={{this.onChange}} />`);
|
||||||
|
|
||||||
|
await triggerEvent(SELECTORS.fileUpload, 'change', { files: [this.file] });
|
||||||
|
assert
|
||||||
|
.dom('[data-test-field-validation="text-file"]')
|
||||||
|
.hasText('There was a problem uploading. Please try again.');
|
||||||
|
assert.propEqual(
|
||||||
|
this.onChange.lastCall.args[0],
|
||||||
|
{
|
||||||
|
filename: '',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
'parent callback function is called with cleared out values'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,5 +3,5 @@ import { clickable, isPresent } from 'ember-cli-page-object';
|
|||||||
export default {
|
export default {
|
||||||
textareaIsPresent: isPresent('[data-test-textarea]'),
|
textareaIsPresent: isPresent('[data-test-textarea]'),
|
||||||
copyButtonIsPresent: isPresent('[data-test-copy-button]'),
|
copyButtonIsPresent: isPresent('[data-test-copy-button]'),
|
||||||
toggleMasked: clickable('[data-test-button]'),
|
toggleMasked: clickable('[data-test-button="toggle-masked"]'),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user