Test GitRepo detail view displays incorrect number of bundles + code improvements and fixes (#10986)

* add cluster tab to git repo details + improve code on fleet dashboard view + fix count of clusters in fleet dashboard view badges + add e2e test for 9866

* address pr comments + remove clusters tab from fleet gitrepo details view
This commit is contained in:
Alexandre Alves 2024-05-20 16:43:19 +01:00 committed by GitHub
parent 91e25ad3e4
commit b70864ace6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 242 additions and 121 deletions

View File

@ -0,0 +1,27 @@
import PagePo from '@/cypress/e2e/po/pages/page.po';
import TabbedPo from '@/cypress/e2e/po/components/tabbed.po';
/**
* Details component for fleet.cattle.io.gitrepo resources
*/
export default class FleetGitRepoDetailsPo extends PagePo {
private static createPath(fleetWorkspace: string, gitRepoName: string) {
return `/c/_/fleet/fleet.cattle.io.gitrepo/${ fleetWorkspace }/${ gitRepoName }`;
}
static goTo(path: string): Cypress.Chainable<Cypress.AUTWindow> {
throw new Error('invalid');
}
constructor(fleetWorkspace: string, gitRepoName: string) {
super(FleetGitRepoDetailsPo.createPath(fleetWorkspace, gitRepoName));
}
gitRepoTabs(): TabbedPo {
return new TabbedPo();
}
bundlesCount(): Cypress.Chainable {
return this.self().find('[data-testid="gitrepo-bundle-summary"] .count').invoke('text');
}
}

View File

@ -3,6 +3,7 @@ import FleetGitRepoList from '@/cypress/e2e/po/lists/fleet/fleet.cattle.io.gitre
import { FleetDashboardPagePo } from '@/cypress/e2e/po/pages/fleet/fleet-dashboard.po';
import ProductNavPo from '@/cypress/e2e/po/side-bars/product-side-nav.po';
import ResourceTablePo from '@/cypress/e2e/po/components/resource-table.po';
import { WorkspaceSwitcherPo } from '@/cypress/e2e/po/components/workspace-switcher.po';
export class FleetGitRepoListPagePo extends PagePo {
static url = `/c/_/fleet/fleet.cattle.io.gitrepo`
@ -34,4 +35,16 @@ export class FleetGitRepoListPagePo extends PagePo {
return resourceTable.sortableTable().detailsPageLinkWithName(elemName).click();
}
resourceTable() {
return new ResourceTablePo(this.self());
}
selectWorkspace(name: string) {
const wsSwitcher = new WorkspaceSwitcherPo();
wsSwitcher.toggle();
return wsSwitcher.clickOptionWithLabel(name);
}
}

View File

@ -1,9 +1,11 @@
import { GitRepoCreatePo } from '@/cypress/e2e/po/pages/fleet/gitrepo-create.po';
import { FleetGitRepoListPagePo } from '@/cypress/e2e/po/pages/fleet/fleet.cattle.io.gitrepo.po';
import FleetGitRepoDetailsPo from '@/cypress/e2e/po/detail/fleet/fleet.cattle.io.gitrepo.po';
import { FleetDashboardPagePo } from '@/cypress/e2e/po/pages/fleet/fleet-dashboard.po';
import { gitRepoCreateRequest } from '@/cypress/e2e/blueprints/fleet/gitrepos';
import { generateFakeClusterDataAndIntercepts } from '@/cypress/e2e/blueprints/nav/fake-cluster';
import PreferencesPagePo from '@/cypress/e2e/po/pages/preferences.po';
import PromptRemove from '@/cypress/e2e/po/prompts/promptRemove.po';
const fakeProvClusterId = 'some-fake-cluster-id';
const fakeMgmtClusterId = 'some-fake-mgmt-id';
@ -13,7 +15,7 @@ describe('Git Repo', { tags: ['@fleet', '@adminUser'] }, () => {
const gitRepoCreatePage = new GitRepoCreatePo('_');
const repoList = [];
before(() => {
beforeEach(() => {
cy.login();
// generate a fake cluster that can be usable in fleet
@ -94,33 +96,102 @@ describe('Git Repo', { tags: ['@fleet', '@adminUser'] }, () => {
// TESTING https://github.com/rancher/dashboard/issues/9984 make sure details page loads fine
listPage.goToDetailsPage('fleet-e2e-test-gitrepo');
gitRepoCreatePage.title().contains('Git 仓库: fleet-e2e-test-gitrepo').should('be.visible');
// https://github.com/rancher/dashboard/issues/9984 reset lang to EN so that delete action can be performed
prefPage.goTo();
prefPage.languageDropdownMenu().checkVisible();
prefPage.languageDropdownMenu().toggle();
prefPage.languageDropdownMenu().isOpened();
cy.intercept('PUT', 'v1/userpreferences/*').as(`prefUpdateEnUs`);
prefPage.languageDropdownMenu().clickOption(1);
cy.wait('@prefUpdateEnUs').then(({ response }) => {
expect(response?.statusCode).to.eq(200);
expect(response?.body.data).to.have.property('locale', 'en-us');
});
prefPage.languageDropdownMenu().isClosed();
})
;
});
// testing https://github.com/rancher/dashboard/issues/9866
it('in git repo details view we should display the correct bundle count', () => {
const listPage = new FleetGitRepoListPagePo();
const basicRepos = [
{
name: 'e2e-git-repo1-test-bundle-count',
repo: 'https://github.com/rancher/fleet-examples.git',
branch: 'master',
path: 'simple'
},
{
name: 'e2e-git-repo2-test-bundle-count',
repo: 'https://github.com/rancher/fleet-examples.git',
branch: 'master',
path: 'single-cluster/helm'
}
];
// create first git-repo in fleet-local
gitRepoCreatePage.goTo();
gitRepoCreatePage.waitForPage();
gitRepoCreatePage.selectWorkspace('fleet-local');
gitRepoCreatePage.setRepoName(basicRepos[0].name);
gitRepoCreatePage.setGitRepoUrl(basicRepos[0].repo);
gitRepoCreatePage.setBranchName(basicRepos[0].branch);
gitRepoCreatePage.gitRepoPaths().setValueAtIndex(basicRepos[0].path, 0);
gitRepoCreatePage.goToNext();
gitRepoCreatePage.create();
// create second git-repo in fleet-local
listPage.waitForPage();
listPage.repoList().create();
gitRepoCreatePage.selectWorkspace('fleet-local');
gitRepoCreatePage.setRepoName(basicRepos[1].name);
gitRepoCreatePage.setGitRepoUrl(basicRepos[1].repo);
gitRepoCreatePage.setBranchName(basicRepos[1].branch);
gitRepoCreatePage.gitRepoPaths().setValueAtIndex(basicRepos[1].path, 0);
gitRepoCreatePage.goToNext();
gitRepoCreatePage.create();
listPage.waitForPage();
listPage.selectWorkspace('fleet-local');
listPage.goToDetailsPage(basicRepos[1].name);
const gitRepoDetails = new FleetGitRepoDetailsPo('fleet-local', basicRepos[1].name);
gitRepoDetails.waitForPage();
gitRepoDetails.gitRepoTabs().clickTabWithSelector('[data-testid="bundles"]');
gitRepoDetails.bundlesCount().should('contain', '1');
});
after(() => {
const prefPage = new PreferencesPagePo();
// https://github.com/rancher/dashboard/issues/9984 reset lang to EN so that delete action can be performed
prefPage.goTo();
prefPage.languageDropdownMenu().checkVisible();
prefPage.languageDropdownMenu().toggle();
prefPage.languageDropdownMenu().isOpened();
cy.intercept('PUT', 'v1/userpreferences/*').as(`prefUpdateEnUs`);
prefPage.languageDropdownMenu().clickOption(1);
cy.wait('@prefUpdateEnUs').then(({ response }) => {
expect(response?.statusCode).to.eq(200);
expect(response?.body.data).to.have.property('locale', 'en-us');
});
prefPage.languageDropdownMenu().isClosed();
const fleetDashboardPage = new FleetDashboardPagePo('_');
FleetDashboardPagePo.goTo();
const fleetLocalResourceTable = fleetDashboardPage.resourceTable('fleet-default');
const fleetDefaultResourceTable = fleetDashboardPage.resourceTable('fleet-default');
fleetLocalResourceTable.sortableTable().deleteItemWithUI('fleet-e2e-test-gitrepo');
fleetDefaultResourceTable.sortableTable().deleteItemWithUI('fleet-e2e-test-gitrepo');
const listPage = new FleetGitRepoListPagePo();
listPage.navTo();
listPage.selectWorkspace('fleet-local');
listPage.resourceTable().sortableTable().rowSelectCtlWithName('e2e-git-repo1-test-bundle-count').set();
listPage.resourceTable().sortableTable().rowSelectCtlWithName('e2e-git-repo2-test-bundle-count').set();
listPage.resourceTable().sortableTable().bulkActionDropDownOpen();
listPage.resourceTable().sortableTable().bulkActionDropDownButton('Delete').click();
const promptRemove = new PromptRemove();
promptRemove.remove();
});
});
});

View File

@ -1,4 +1,5 @@
<script>
import Loading from '@shell/components/Loading';
import ResourceTabs from '@shell/components/form/ResourceTabs';
import FleetSummary from '@shell/components/fleet/FleetSummary';
import { Banner } from '@components/Banner';
@ -14,6 +15,7 @@ export default {
name: 'DetailGitRepo',
components: {
Loading,
FleetResources,
FleetSummary,
Banner,
@ -31,21 +33,22 @@ export default {
data() {
return {
allFleet: [],
allFleetClusters: [],
allBundles: [],
allBundleDeployments: [],
};
},
computed: {
gitRepoHasClusters() {
return this.value.status.desiredReadyClusters;
return this.value?.clusterResourceStatus?.length;
},
clusterSchema() {
return this.$store.getters['management/schemaFor'](FLEET.CLUSTER);
},
harvesterClusters() {
const harvester = {};
this.allFleet.forEach((c) => {
this.allFleetClusters.forEach((c) => {
if (isHarvesterCluster(c)) {
harvester[c.metadata.name] = c;
}
@ -53,7 +56,6 @@ export default {
return harvester;
},
bundleCounts() {
return resourceCounts(this.$store, FLEET.BUNDLE);
},
@ -86,7 +88,7 @@ export default {
type: FLEET.BUNDLE_DEPLOYMENT
},
allFleet: {
allFleetClusters: {
inStoreType: 'management',
type: FLEET.CLUSTER
},
@ -98,14 +100,18 @@ export default {
this.allBundleDeployments = allDispatches.allBundleDeployments || [];
this.allBundles = allDispatches.allBundles || [];
this.allFleet = allDispatches.allFleet || [];
this.allFleetClusters = allDispatches.allFleetClusters || [];
},
};
</script>
<template>
<div class="mt-20">
<Loading v-if="$fetchState.pending" />
<div
v-else
class="mt-20"
>
<FleetSummary
v-if="gitRepoHasClusters"
:value="value"

View File

@ -105,6 +105,8 @@ export default class GitRepo extends SteveModel {
const groups = workspace?.clusterGroups || [];
if (workspace?.id === 'fleet-local') {
// should we be getting the clusters from workspace.clusters instead of having to rely on the groups,
// which takes an additional request to be done on the Fleet dashboard screen?
const local = findBy(groups, 'id', 'fleet-local/default');
if (local) {
@ -281,7 +283,6 @@ export default class GitRepo extends SteveModel {
return {
mode,
// i18n-uses fleet.gitRepo.targetDisplay.*
modeDisplay: this.t(`fleet.gitRepo.targetDisplay."${ mode }"`),
cluster,
clusterGroup,
@ -315,6 +316,14 @@ export default class GitRepo extends SteveModel {
return 0;
}
get targetClustersReady() {
if (this.targetClusters && this.targetClusters.length) {
return this.targetClusters.filter((cluster) => cluster.state === 'active');
}
return 0;
}
get bundleDeployments() {
const bds = this.$getters['all'](FLEET.BUNDLE_DEPLOYMENT);

View File

@ -32,6 +32,10 @@ export default {
return !!schema?.links?.collection;
}
},
clusterGroups: {
inStoreType: 'management',
type: FLEET.CLUSTER_GROUP
},
allBundles: {
inStoreType: 'management',
type: FLEET.BUNDLE,
@ -39,6 +43,10 @@ export default {
gitRepos: {
inStoreType: 'management',
type: FLEET.GIT_REPO,
},
fleetClusters: {
inStoreType: 'management',
type: FLEET.CLUSTER,
}
}, this.$store);
@ -56,7 +64,8 @@ export default {
data() {
return {
headers: [
admissableAreas: ['clusters', 'bundles', 'resources'],
headers: [
{
name: 'name',
labelKey: 'tableHeaders.repoName',
@ -109,7 +118,6 @@ export default {
}
// When user doesn't have access to the workspaces fall back to namespaces
return this.allNamespaces.filter((item) => {
return item.metadata.annotations[WORKSPACE_ANNOTATION] === WORKSPACE;
}).map(( obj ) => {
@ -151,112 +159,82 @@ export default {
});
},
getStatusInfo(area, row) {
const defaultStatusInfo = {
badgeClass: `${ STATES[STATES_ENUM.NOT_READY].color } badge-class-default`,
icon: STATES[STATES_ENUM.NOT_READY].compoundIcon
};
// classes are defined in the themes SASS files...
switch (area) {
case 'clusters':
if (row.clusterInfo?.ready === row.clusterInfo?.total && row.clusterInfo?.ready) {
return {
badgeClass: STATES[STATES_ENUM.ACTIVE].color,
icon: STATES[STATES_ENUM.ACTIVE].compoundIcon
};
}
return this.getBadgeClassAndIcon(area, row) || defaultStatusInfo;
},
getBadgeClassAndIcon(area, row) {
let group;
if (!this.admissableAreas.includes(area)) {
return false;
}
if (area === 'clusters') {
group = row.targetClusters;
} else if (area === 'bundles') {
group = row.bundles;
} else if (area === 'resources') {
group = row.status?.resources;
}
if (group?.length && group?.every((item) => item.state?.toLowerCase() === STATES_ENUM.ACTIVE)) {
return {
badgeClass: `${ STATES[STATES_ENUM.NOT_READY].color } badge-class-area-clusters`,
icon: STATES[STATES_ENUM.NOT_READY].compoundIcon
badgeClass: STATES[STATES_ENUM.ACTIVE].color ? STATES[STATES_ENUM.ACTIVE].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
icon: STATES[STATES_ENUM.ACTIVE].compoundIcon ? STATES[STATES_ENUM.ACTIVE].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
case 'bundles':
if (row.bundles?.length && row.bundles?.every((bundle) => bundle.state?.toLowerCase() === STATES_ENUM.ACTIVE)) {
return {
badgeClass: STATES[STATES_ENUM.ACTIVE].color ? STATES[STATES_ENUM.ACTIVE].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
icon: STATES[STATES_ENUM.ACTIVE].compoundIcon ? STATES[STATES_ENUM.ACTIVE].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
}
if (row.bundles?.length && row.bundles?.some((bundle) => bundle.state?.toLowerCase() === STATES_ENUM.ERR_APPLIED)) {
return {
badgeClass: STATES[STATES_ENUM.ERR_APPLIED].color ? STATES[STATES_ENUM.ERR_APPLIED].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
icon: STATES[STATES_ENUM.ERR_APPLIED].compoundIcon ? STATES[STATES_ENUM.ERR_APPLIED].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
}
if (row.bundles?.length && row.bundles?.some((bundle) => bundle.state?.toLowerCase() === STATES_ENUM.NOT_READY)) {
return {
badgeClass: STATES[STATES_ENUM.NOT_READY].color ? STATES[STATES_ENUM.NOT_READY].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
icon: STATES[STATES_ENUM.NOT_READY].compoundIcon ? STATES[STATES_ENUM.NOT_READY].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
}
if (row.bundlesReady?.length === row.bundles?.length && row.bundlesReady && row.bundles?.length) {
return {
badgeClass: STATES[STATES_ENUM.ACTIVE].color,
icon: STATES[STATES_ENUM.ACTIVE].compoundIcon
};
}
}
if (group?.length && group?.some((item) => item.state?.toLowerCase() === STATES_ENUM.ERR_APPLIED)) {
return {
badgeClass: `${ STATES[STATES_ENUM.NOT_READY].color } badge-class-area-bundles`,
icon: STATES[STATES_ENUM.NOT_READY].compoundIcon
badgeClass: STATES[STATES_ENUM.ERR_APPLIED].color ? STATES[STATES_ENUM.ERR_APPLIED].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
icon: STATES[STATES_ENUM.ERR_APPLIED].compoundIcon ? STATES[STATES_ENUM.ERR_APPLIED].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
case 'resources':
if (row.status?.resources?.length && row.status?.resources?.every((resource) => resource.state?.toLowerCase() === STATES_ENUM.ACTIVE)) {
return {
badgeClass: STATES[STATES_ENUM.ACTIVE].color ? STATES[STATES_ENUM.ACTIVE].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
icon: STATES[STATES_ENUM.ACTIVE].compoundIcon ? STATES[STATES_ENUM.ACTIVE].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
}
if (row.status?.resources?.length && row.status?.resources?.some((resource) => resource.state?.toLowerCase() === STATES_ENUM.ERR_APPLIED)) {
return {
badgeClass: STATES[STATES_ENUM.ERR_APPLIED].color ? STATES[STATES_ENUM.ERR_APPLIED].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
icon: STATES[STATES_ENUM.ERR_APPLIED].compoundIcon ? STATES[STATES_ENUM.ERR_APPLIED].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
}
if (row.status?.resources?.length && row.status?.resources?.some((resource) => resource.state?.toLowerCase() === STATES_ENUM.NOT_READY)) {
return {
badgeClass: STATES[STATES_ENUM.NOT_READY].color ? STATES[STATES_ENUM.NOT_READY].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
icon: STATES[STATES_ENUM.NOT_READY].compoundIcon ? STATES[STATES_ENUM.NOT_READY].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
}
}
if (group?.length && group?.some((item) => item.state?.toLowerCase() === STATES_ENUM.NOT_READY)) {
return {
badgeClass: STATES[STATES_ENUM.NOT_READY].color ? STATES[STATES_ENUM.NOT_READY].color : `${ STATES[STATES_ENUM.UNKNOWN].color } bg-unmapped-state`,
icon: STATES[STATES_ENUM.NOT_READY].compoundIcon ? STATES[STATES_ENUM.NOT_READY].compoundIcon : `${ STATES[STATES_ENUM.UNKNOWN].compoundIcon } unmapped-icon`
};
}
if (area === 'resources') {
if (row.status?.resourceCounts?.desiredReady === row.status?.resourceCounts?.ready && row.status?.resourceCounts?.desiredReady) {
return {
badgeClass: STATES[STATES_ENUM.ACTIVE].color,
icon: STATES[STATES_ENUM.ACTIVE].compoundIcon
};
}
return {
badgeClass: `${ STATES[STATES_ENUM.NOT_READY].color } badge-class-area-resources`,
icon: STATES[STATES_ENUM.NOT_READY].compoundIcon
};
default:
return {
badgeClass: `${ STATES[STATES_ENUM.NOT_READY].color } badge-class-default`,
icon: STATES[STATES_ENUM.NOT_READY].compoundIcon
};
}
return {
badgeClass: `${ STATES[STATES_ENUM.NOT_READY].color } badge-class-area-${ area }`,
icon: STATES[STATES_ENUM.NOT_READY].compoundIcon
};
},
getTooltipInfo(area, row) {
switch (area) {
case 'clusters':
if (row.clusterInfo?.total) {
return `Ready: ${ row.clusterInfo?.ready }<br>Total: ${ row.clusterInfo?.total }`;
}
let group;
return '';
case 'bundles':
if (row.bundles?.length) {
return this.generateTooltipData(row.bundles);
}
return '';
case 'resources':
if (row.status?.resources?.length) {
return this.generateTooltipData(row.status?.resources);
}
return '';
default:
if (!this.admissableAreas.includes(area)) {
return {};
}
if (area === 'clusters') {
group = row.targetClusters;
} else if (area === 'bundles') {
group = row.bundles;
} else if (area === 'resources') {
group = row.status?.resources;
}
if (group?.length) {
return this.generateTooltipData(group);
}
return '';
},
generateTooltipData(data) {
const infoObj = {};
@ -276,6 +254,23 @@ export default {
return tooltipData;
},
getBadgeValue(area, row) {
let value;
if (!this.admissableAreas.includes(area)) {
return 'N/A';
}
if (area === 'clusters') {
value = `${ row.targetClustersReady?.length || '0' }/${ row.targetClusters?.length || '?' }`;
} else if (area === 'bundles') {
value = `${ row.bundlesReady?.length || '0' }/${ row.bundles?.length || '?' }`;
} else if (area === 'resources') {
value = `${ row.status?.resourceCounts?.ready || '0' }/${ row.status?.resourceCounts?.desiredReady || '?' }`;
}
return value;
},
toggleCollapse(val, key) {
this.$set(this.isCollapsed, key, val);
},
@ -416,7 +411,7 @@ export default {
:tooltip-text="getTooltipInfo('clusters', row)"
:badge-class="getStatusInfo('clusters', row).badgeClass"
:icon="getStatusInfo('clusters', row).icon"
:value="`${ row.clusterInfo.ready }/${ row.clusterInfo.total }`"
:value="getBadgeValue('clusters', row)"
/>
</template>
<template #cell:bundlesReady="{row}">
@ -427,7 +422,7 @@ export default {
:tooltip-text="getTooltipInfo('bundles', row)"
:badge-class="getStatusInfo('bundles', row).badgeClass"
:icon="getStatusInfo('bundles', row).icon"
:value="`${ row.bundlesReady.length || 0 }/${ row.bundles.length }`"
:value="getBadgeValue('bundles', row)"
/>
</template>
<template #cell:resourcesReady="{row}">
@ -436,7 +431,7 @@ export default {
:tooltip-text="getTooltipInfo('resources', row)"
:badge-class="getStatusInfo('resources', row).badgeClass"
:icon="getStatusInfo('resources', row).icon"
:value="`${ row.status.resourceCounts.ready }/${ row.status.resourceCounts.desiredReady }`"
:value="getBadgeValue('resources', row)"
/>
</template>