diff --git a/generators/client/files.js b/generators/client/files.js index 65254fc80..0ab463644 100644 --- a/generators/client/files.js +++ b/generators/client/files.js @@ -32,6 +32,7 @@ const svelteFiles = { e2e: [ { templates: [ + 'cypress/fixtures/integration-test.png', 'cypress/integration/footer.spec.js', 'cypress/integration/home.spec.js', 'cypress/integration/navbar.spec.js', @@ -145,10 +146,11 @@ const svelteFiles = { 'admin/logger/logger-service.js', 'admin/logger/LoggerTable.svelte', 'admin/logger/LoggerTable.spec.js', + 'page/BinaryRecord.svelte', 'page/Page.svelte', 'page/PageHeader.svelte', 'page/Form.svelte', - '/page/SearchForm.svelte', + 'page/SearchForm.svelte', 'page/Record.svelte', 'Alert.svelte', 'Button.svelte', @@ -181,6 +183,7 @@ const svelteFiles = { 'table/TableHeader.svelte', 'table/TableRow.svelte', 'utils/date.js', + 'utils/data-util.js', 'utils/env.js', 'utils/request.js', 'utils/validator.js', diff --git a/generators/client/templates/svelte/cypress/fixtures/integration-test.png b/generators/client/templates/svelte/cypress/fixtures/integration-test.png new file mode 100755 index 000000000..24baf78ce Binary files /dev/null and b/generators/client/templates/svelte/cypress/fixtures/integration-test.png differ diff --git a/generators/client/templates/svelte/src/main/webapp/app/lib/page/BinaryRecord.svelte.ejs b/generators/client/templates/svelte/src/main/webapp/app/lib/page/BinaryRecord.svelte.ejs new file mode 100644 index 000000000..2092376bc --- /dev/null +++ b/generators/client/templates/svelte/src/main/webapp/app/lib/page/BinaryRecord.svelte.ejs @@ -0,0 +1,27 @@ + + +{#if field} +
+ {#if fieldType === 'image'} + {name} + {:else} + View + {/if} +
+ {fieldContentType}, + {byteSize(field)} +{/if} diff --git a/generators/client/templates/svelte/src/main/webapp/app/lib/utils/data-util.js.ejs b/generators/client/templates/svelte/src/main/webapp/app/lib/utils/data-util.js.ejs new file mode 100644 index 000000000..cf869c689 --- /dev/null +++ b/generators/client/templates/svelte/src/main/webapp/app/lib/utils/data-util.js.ejs @@ -0,0 +1,42 @@ +export function byteSize(base64String) { + return formatAsBytes(size(base64String)) +} + +export function openFile(data, contentType) { + const byteCharacters = atob(data) + const byteNumbers = new Array(byteCharacters.length) + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i) + } + const byteArray = new Uint8Array(byteNumbers) + const blob = new Blob([byteArray], { + type: contentType, + }) + const fileURL = window.URL.createObjectURL(blob) + const win = window.open(fileURL) + win.onload = function () { + URL.revokeObjectURL(fileURL) + } +} + +function endsWith(suffix, str) { + return str.includes(suffix, str.length - suffix.length) +} + +function paddingSize(value) { + if (endsWith('==', value)) { + return 2 + } + if (endsWith('=', value)) { + return 1 + } + return 0 +} + +function size(value) { + return (value.length / 4) * 3 - paddingSize(value) +} + +function formatAsBytes(size) { + return size.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + ' bytes' +} diff --git a/generators/client/templates/svelte/src/main/webapp/app/lib/utils/date.js.ejs b/generators/client/templates/svelte/src/main/webapp/app/lib/utils/date.js.ejs index 7e73fc6dd..c60cb7c8f 100644 --- a/generators/client/templates/svelte/src/main/webapp/app/lib/utils/date.js.ejs +++ b/generators/client/templates/svelte/src/main/webapp/app/lib/utils/date.js.ejs @@ -1,9 +1,36 @@ -import { format, formatDistanceToNow } from 'date-fns' +import { + format, + formatDistanceToNow, + formatDuration, + intervalToDuration, +} from 'date-fns' export function formatDate(dateToFormat) { - return dateToFormat ? format(new Date(Date.parse(dateToFormat)), 'MM/dd/yyyy HH:mm') : '-' + return dateToFormat + ? format(new Date(Date.parse(dateToFormat)), 'MM/dd/yyyy HH:mm') + : '-' } export function formatDistance(dateToFormat) { - return dateToFormat ? formatDistanceToNow(new Date(Date.parse(dateToFormat)), {addSuffix: true}) : '-' + return dateToFormat + ? formatDistanceToNow(new Date(Date.parse(dateToFormat)), { + addSuffix: true, + }) + : '-' +} + +export function formatDurationType(durationToFormat) { + if (durationToFormat) { + let valueInSeconds = durationToFormat.substring( + durationToFormat.indexOf('.') + 1, + durationToFormat.length - 1 + ) + return valueInSeconds + ? formatDuration( + intervalToDuration({ start: 0, end: valueInSeconds * 1000 }) + ) + : '-' + } else { + return '-' + } } diff --git a/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-delete.spec.js.ejs b/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-delete.spec.js.ejs index 812c29c7b..b214a2bfc 100644 --- a/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-delete.spec.js.ejs +++ b/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-delete.spec.js.ejs @@ -1,42 +1,73 @@ +<%_ + const entityFakeData = generateFakeData('cypress'); + const containsBinaryField = fields.find(field => (field.fieldTypeBinary && !field.blobContentTypeText)); +_%> describe('<%= entityAngularName %> delete dialog page', () => { - let randomPrefix let dynamicId beforeEach(() => { cy.unregisterServiceWorkers() - randomPrefix = 'test' + new Date().getTime() cy.loginByApi( Cypress.env('adminUsername'), Cypress.env('adminPassword') ) - - cy.save('api/<%= entityApiUrl %>', { - <%_ - for (field of fields.filter(field => !field.id)) { - _%> - <%_ if (field.fieldTypeTimed) { _%> - <%= field.fieldName %>: new Date(), - <%_ } else { _%> - <%= field.fieldName %>: randomPrefix, - <%_ } _%> - <%_ } _%> +<%_ + if (containsBinaryField) { +_%> + cy.fixture('integration-test.png') + .then($blob => { + return <% } %>cy.save('api/<%= entityApiUrl %>', { + <%_ + for (field of fields.filter(field => !field.id)) { + const fieldValue = !entityFakeData ? field.generateFakeData('cypress') : entityFakeData[field.fieldName]; + if (fieldValue === undefined) { + warning(`Error generating a value for field ${field.fieldName}`); + } + _%> + <%_ if (field.fieldTypeBoolean) { _%> + <%= field.fieldName %>: true, + <%_ } else if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + <%= field.fieldName %>: $blob, + <%= field.fieldName %>ContentType: 'image/png', + <%_ } else if (field.fieldTypeString || field.fieldTypeUUID) { _%> + <%= field.fieldName %>: '<%= fieldValue %>', + <%_ } else if (field.fieldTypeLocalDate) { _%> + <%= field.fieldName %>: '<%= fieldValue %>' , + <%_ } else if (field.fieldTypeTimed) { _%> + <%= field.fieldName %>: '<%= fieldValue %>:00.000Z' , + <%_ } else if (field.fieldTypeDuration) { _%> + <%= field.fieldName %>: 'PT0.000052484S', + <%_ } else if (field.fieldTypeNumeric) { _%> + <%= field.fieldName %>: <%= fieldValue %>, + <%_ } else { _%> + <%= field.fieldName %>: '<%= fieldValue %>', + <%_ } _%> + <%_ } _%> +<%_ if (containsBinaryField) { _%> + }) +<%_ } _%> }).then(res => { dynamicId = res.id - }) - - cy.visit('/entities/<%= entityFolderName %>') - - cy.getBySel('<%= entityInstance %>Table') - .contains('td', randomPrefix) - .parent() - .trigger('mouseenter') - .within($tr => { - cy.root() - .get('td') - .children() - .getByName('delete<%= entityAngularName %>Btn') - .click() + cy.intercept('**/api/<%= entityApiUrl %>*').as('get<%= entityClassPluralHumanized %>') + cy.visit('/entities/<%= entityFolderName %>') + cy.wait('@get<%= entityClassPluralHumanized %>') + // eslint-disable-next-line + cy.wait(100) + cy.getBySel('<%= entityInstance %>Table') + .get('td:nth-child(1)').each(($el, index, $list) => { + const text = $el.text(); + if(text.includes(dynamicId)) { + cy.getBySel('<%= entityInstance %>Table') + .get('td:nth-child(1)') + .eq(index) + .parent() + .trigger('mouseenter') + .within($tr => { + cy.root().get('td').children().getByName('delete<%= entityAngularName %>Btn').click() + }) + } }) + }) }) afterEach(() => { diff --git a/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-list.spec.js.ejs b/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-list.spec.js.ejs index edcd58deb..8b1570065 100644 --- a/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-list.spec.js.ejs +++ b/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-list.spec.js.ejs @@ -1,8 +1,10 @@ +<%_ + const entityFakeData = generateFakeData('cypress'); + const containsBinaryField = fields.find(field => (field.fieldTypeBinary && !field.blobContentTypeText)); +_%> describe('<%= entityAngularName %> list page', () => { - let randomPrefix let dynamicId <%_ if (!paginationNo || searchEngine) { _%> - let randomPrefix2 let dynamicId2 <%_ } _%> beforeEach(() => { @@ -13,32 +15,77 @@ describe('<%= entityAngularName %> list page', () => { ) <%_ if (!paginationNo || searchEngine) { _%> // create another <%= entityAngularName %> to test sort implementation - randomPrefix2 = 'zest' + new Date().getTime() - cy.save('api/<%= entityApiUrl %>', { - <%_ - for (field of fields.filter(field => !field.id)) { - _%> - <%_ if (field.fieldTypeTimed) { _%> - <%= field.fieldName %>: new Date(), - <%_ } else { _%> - <%= field.fieldName %>: randomPrefix2, +<%_ if (containsBinaryField) { _%> + cy.fixture('integration-test.png') + .then($blob => { + return <% } %>cy.save('api/<%= entityApiUrl %>', { + <%_ + for (field of fields.filter(field => !field.id)) { + const fieldValue = !entityFakeData ? field.generateFakeData('cypress') : entityFakeData[field.fieldName]; + if (fieldValue === undefined) { + warning(`Error generating a value for field ${field.fieldName}`); + } + _%> + <%_ if (field.fieldTypeBoolean) { _%> + <%= field.fieldName %>: true, + <%_ } else if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + <%= field.fieldName %>: $blob, + <%= field.fieldName %>ContentType: 'image/png', + <%_ } else if (field.fieldTypeString || field.fieldTypeUUID) { _%> + <%= field.fieldName %>: '<%= fieldValue %>', + <%_ } else if (field.fieldTypeLocalDate) { _%> + <%= field.fieldName %>: '<%= fieldValue %>' , + <%_ } else if (field.fieldTypeTimed) { _%> + <%= field.fieldName %>: '<%= fieldValue %>:00.000Z' , + <%_ } else if (field.fieldTypeDuration) { _%> + <%= field.fieldName %>: 'PT0.000052484S', + <%_ } else if (field.fieldTypeNumeric) { _%> + <%= field.fieldName %>: <%= fieldValue %>, + <%_ } else { _%> + <%= field.fieldName %>: '<%= fieldValue %>', + <%_ } _%> <%_ } _%> - <%_ } _%> +<%_ if (containsBinaryField) { _%> + }) +<%_ } _%> }).then(res => { dynamicId2 = res.id }) <%_ } _%> - randomPrefix = 'test' + new Date().getTime() - cy.save('api/<%= entityApiUrl %>', { - <%_ - for (field of fields.filter(field => !field.id)) { - _%> - <%_ if (field.fieldTypeTimed) { _%> - <%= field.fieldName %>: new Date(), - <%_ } else { _%> - <%= field.fieldName %>: randomPrefix, + +<%_ if (containsBinaryField) { _%> + cy.fixture('integration-test.png') + .then($blob => { + return <% } %>cy.save('api/<%= entityApiUrl %>', { + <%_ + for (field of fields.filter(field => !field.id)) { + const fieldValue = !entityFakeData ? field.generateFakeData('cypress') : entityFakeData[field.fieldName]; + if (fieldValue === undefined) { + warning(`Error generating a value for field ${field.fieldName}`); + } + _%> + <%_ if (field.fieldTypeBoolean) { _%> + <%= field.fieldName %>: true, + <%_ } else if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + <%= field.fieldName %>: $blob, + <%= field.fieldName %>ContentType: 'image/png', + <%_ } else if (field.fieldTypeString || field.fieldTypeUUID) { _%> + <%= field.fieldName %>: '<%= fieldValue %>', + <%_ } else if (field.fieldTypeLocalDate) { _%> + <%= field.fieldName %>: '<%= fieldValue %>' , + <%_ } else if (field.fieldTypeTimed) { _%> + <%= field.fieldName %>: '<%= fieldValue %>:00.000Z' , + <%_ } else if (field.fieldTypeDuration) { _%> + <%= field.fieldName %>: 'PT0.000052484S', + <%_ } else if (field.fieldTypeNumeric) { _%> + <%= field.fieldName %>: <%= fieldValue %>, + <%_ } else { _%> + <%= field.fieldName %>: '<%= fieldValue %>', + <%_ } _%> <%_ } _%> - <%_ } _%> +<%_ if (containsBinaryField) { _%> + }) +<%_ } _%> }).then(res => { dynamicId = res.id cy.intercept('**/api/<%= entityApiUrl %>*').as('get<%= entityClassPluralHumanized %>') diff --git a/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-view.spec.js.ejs b/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-view.spec.js.ejs index b4efdec39..f9f409043 100644 --- a/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-view.spec.js.ejs +++ b/generators/entity-client/templates/svelte/cypress/integration/entities/entity/entity-view.spec.js.ejs @@ -1,25 +1,50 @@ +<%_ + const entityFakeData = generateFakeData('cypress'); + const containsBinaryField = fields.find(field => (field.fieldTypeBinary && !field.blobContentTypeText)); +_%> describe('<%= entityAngularName %> view details page', () => { - let randomPrefix let dynamicId beforeEach(() => { cy.unregisterServiceWorkers() - randomPrefix = 'test' + new Date().getTime() cy.loginByApi( Cypress.env('adminUsername'), Cypress.env('adminPassword') ) - cy.save('api/<%= entityApiUrl %>', { - <%_ - for (field of fields.filter(field => !field.id)) { - _%> - <%_ if (field.fieldTypeTimed) { _%> - <%= field.fieldName %>: new Date(), - <%_ } else { _%> - <%= field.fieldName %>: randomPrefix, +<%_ if (containsBinaryField) { _%> + cy.fixture('integration-test.png') + .then($blob => { + return <% } %>cy.save('api/<%= entityApiUrl %>', { + <%_ + for (field of fields.filter(field => !field.id)) { + const fieldValue = !entityFakeData ? field.generateFakeData('cypress') : entityFakeData[field.fieldName]; + if (fieldValue === undefined) { + warning(`Error generating a value for field ${field.fieldName}`); + } + _%> + <%_ if (field.fieldTypeBoolean) { _%> + <%= field.fieldName %>: true, + <%_ } else if (field.fieldTypeBinary && !field.blobContentTypeText) { _%> + <%= field.fieldName %>: $blob, + <%= field.fieldName %>ContentType: 'image/png', + <%_ } else if (field.fieldTypeString || field.fieldTypeUUID) { _%> + <%= field.fieldName %>: '<%= fieldValue %>', + <%_ } else if (field.fieldTypeLocalDate) { _%> + <%= field.fieldName %>: '<%= fieldValue %>' , + <%_ } else if (field.fieldTypeTimed) { _%> + <%= field.fieldName %>: '<%= fieldValue %>:00.000Z' , + <%_ } else if (field.fieldTypeDuration) { _%> + <%= field.fieldName %>: 'PT0.000052484S', + <%_ } else if (field.fieldTypeNumeric) { _%> + <%= field.fieldName %>: <%= fieldValue %>, + <%_ } else { _%> + <%= field.fieldName %>: '<%= fieldValue %>', + <%_ } _%> <%_ } _%> - <%_ } _%> +<%_ if (containsBinaryField) { _%> + }) +<%_ } _%> }).then(res => { dynamicId = res.id cy.visit(`/entities/<%= entityFolderName %>/${dynamicId}/view`) @@ -42,13 +67,6 @@ describe('<%= entityAngularName %> view details page', () => { cy.root() .get('.table-cell') .should('have.length', <%= (fields.length - 1) * 2 %>) - <%_ - for (let loopIndex = 1; loopIndex > fields.length * 2; loopIndex= loopIndex + 2) { - _%> - .get('.table-cell') - .eq(<%= loopIndex %>) - .should('contain', randomPrefix) - <%_ }_%> }) }) diff --git a/generators/entity-client/templates/svelte/src/main/webapp/app/lib/entities/entity/EntityTable.svelte.ejs b/generators/entity-client/templates/svelte/src/main/webapp/app/lib/entities/entity/EntityTable.svelte.ejs index 28d916c08..85508df3c 100644 --- a/generators/entity-client/templates/svelte/src/main/webapp/app/lib/entities/entity/EntityTable.svelte.ejs +++ b/generators/entity-client/templates/svelte/src/main/webapp/app/lib/entities/entity/EntityTable.svelte.ejs @@ -5,9 +5,16 @@ import TableData from '$lib/table/TableData.svelte' import TableHeader from '$lib/table/TableHeader.svelte' import TableRow from '$lib/table/TableRow.svelte' -<%_ const timedFieldType = fields.find(field => field.fieldTypeTimed); - if (timedFieldType) { _%> - import { formatDate } from '$lib/utils/date' +<%_ const timedFieldType = fields.find(field => (field.fieldTypeTimed || field.fieldTypeLocalDate)); + const durationFieldType = fields.find(field => field.fieldTypeDuration); + const bothTimedAndDuration = timedFieldType && durationFieldType; + const binaryFieldType = fields.find(field => ((field.fieldTypeBinary && field.blobContentTypeImage) || (field.fieldTypeBinary && field.blobContentTypeAny))); + if (timedFieldType || durationFieldType ) { _%> + import { <%_ if (timedFieldType) { _%>formatDate <%_ } _%><%_ if (bothTimedAndDuration) { _%>, <%_ } else { _%><%_ } _%><%_ if (durationFieldType) { _%> formatDurationType <%_ } _%> } from '$lib/utils/date' +<%_ } + if (binaryFieldType) { +_%> + import BinaryRecord from '$lib/page/BinaryRecord.svelte' <%_ } _%> export let <%= entityInstancePlural %> = [] <%_ if (!paginationNo) { _%> @@ -42,7 +49,6 @@ <%_ } _%> <%_ } _%> - @@ -50,24 +56,53 @@ {<%= entityInstance %>.id} <%_ + // exclude id field in last field index calculation + const lastFieldIndex = fields.length - 1; + let fieldIndex = 1; for (field of fields.filter(field => !field.id)) { _%> - <%_ if (field.fieldTypeTimed) { _%> - {formatDate(<%=entityInstance %>.<%= field.fieldName %>)} + classes="min-w-48 {showActions ? 'relative' : ''}"<%_ } _%>> + <%_ if(fieldIndex === lastFieldIndex) { _%> +
+ <%_ }_%> + <%_ if (field.fieldTypeBinary && field.blobContentTypeImage) { _%> + + <%_ } else if (field.fieldTypeBinary && field.blobContentTypeAny) { _%> + + <%_ } else if (field.fieldTypeTimed || field.fieldTypeLocalDate) { _%> + {formatDate(<%=entityInstance %>.<%= field.fieldName %>)} + <%_ } else if (field.fieldTypeDuration) { _%> + {formatDurationType(<%=entityInstance %>.<%= field.fieldName %>)} <%_ } else { _%> - {<%=entityInstance %>.<%= field.fieldName %>} + {<%=entityInstance %>.<%= field.fieldName %>} <%_ } _%> - <%_ } _%> - -
 
- <<%= entityAngularName %>ListActions - <%= entityInstance %>="{<%= entityInstance %>}" - showActions="{showActions}" - on:view<%= entityInstance %> - on:update<%= entityInstance %> - on:delete<%= entityInstance %> - /> + <%_ if(fieldIndex === lastFieldIndex) { _%> +
+
+ <<%= entityAngularName %>ListActions + <%= entityInstance %>="{<%= entityInstance %>}" + showActions="{showActions}" + on:view<%= entityInstance %> + on:update<%= entityInstance %> + on:delete<%= entityInstance %> + /> +
+ <%_ }_%> + <%_ + fieldIndex++; + } _%>
{:else} diff --git a/generators/entity-client/templates/svelte/src/main/webapp/app/routes/entities/entity/[id]/view.svelte.ejs b/generators/entity-client/templates/svelte/src/main/webapp/app/routes/entities/entity/[id]/view.svelte.ejs index b6f356df3..7d1457ad6 100644 --- a/generators/entity-client/templates/svelte/src/main/webapp/app/routes/entities/entity/[id]/view.svelte.ejs +++ b/generators/entity-client/templates/svelte/src/main/webapp/app/routes/entities/entity/[id]/view.svelte.ejs @@ -9,9 +9,16 @@ import Record from '$lib/page/Record.svelte' import Button from '$lib/Button.svelte' import Icon from '$lib/Icon.svelte' -<%_ const timedFieldType = fields.find(field => field.fieldTypeTimed); - if (timedFieldType) { _%> - import { formatDate } from '$lib/utils/date' +<%_ const timedFieldType = fields.find(field => (field.fieldTypeTimed || field.fieldTypeLocalDate)); + const durationFieldType = fields.find(field => field.fieldTypeDuration); + const bothTimedAndDuration = timedFieldType && durationFieldType; + const binaryFieldType = fields.find(field => ((field.fieldTypeBinary && field.blobContentTypeImage) || (field.fieldTypeBinary && field.blobContentTypeAny))); + if (timedFieldType || durationFieldType ) { _%> + import { <%_ if (timedFieldType) { _%> formatDate<%_ } _%><%_ if (bothTimedAndDuration) { _%>, <%_ } else { _%> <%_ } _%><%_ if (durationFieldType) { _%> formatDurationType <%_ } _%> } from '$lib/utils/date' +<%_ } + if (binaryFieldType) { +_%> + import BinaryRecord from '$lib/page/BinaryRecord.svelte' <%_ } _%> $: id = $page && $page.params && $page.params.id @@ -43,8 +50,23 @@ _%> classes="border-t"<% } %>> <%= field.fieldNameHumanized %> - <%_ if (field.fieldTypeTimed) { _%> + <%_ if (field.fieldTypeBinary && field.blobContentTypeImage) { _%> + + <%_ } else if (field.fieldTypeBinary && field.blobContentTypeAny) { _%> + + <%_ } else if (field.fieldTypeTimed || field.fieldTypeLocalDate) { _%> {formatDate(<%= entityInstance %>.<%= field.fieldName %>)} + <%_ } else if (field.fieldTypeDuration) { _%> + {formatDurationType(<%= entityInstance %>.<%= field.fieldName %>)} <%_ } else { _%> {<%= entityInstance %>.<%= field.fieldName %>} <%_ } _%>