Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions test/nuts/digitalExperienceBundleWithWebapps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# WebApp DigitalExperienceBundle NUT Tests

This directory contains NUT (Non-Unit Tests) for testing web_app DigitalExperienceBundle deployments with path-based fullNames.

## Test Files

### 1. `webapp.nut.ts`

Basic deployment and retrieval tests for webapp bundles.

**What it tests**:

- Deploy webapp bundle without "not found in local project" warnings
- Path-based fullNames for web_app files (e.g., `web_app/WebApp/src/App.js`)
- Retrieve webapp bundle with path-based fullNames
- .forceignore pattern matching for webapp files

## Test Project Structure

The test project includes:

```
force-app/main/default/digitalExperiences/web_app/WebApp/
├── webapp.json
├── public/
│ ├── index.html
│ └── images/
│ ├── icon.png
│ ├── photo.jpg
│ └── logo.svg
└── src/
├── App.css
├── App.js
├── index.css
└── index.js
```

## Running Tests

Run all webapp NUT tests:

```bash
yarn test:nuts:deb-webapp
```

Run individual tests:

```bash
yarn mocha test/nuts/digitalExperienceBundleWithWebapps/webapp.nut.ts
```

## Technical Details

### FullName Format

Web_app files use path-based fullNames:

```
web_app/WebApp/src/App.js
└──────┘└─────┘└────────┘
baseType bundle relative path
```

## Related Files

- **Implementation**: `/src/resolve/adapters/digitalExperienceSourceAdapter.ts`
- **Deploy Logic**: `/src/client/deployMessages.ts`
- **Path Name Util**: `/src/client/utils.ts` (`computeWebAppPathName`)
61 changes: 61 additions & 0 deletions test/nuts/digitalExperienceBundleWithWebapps/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2025, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { join } from 'node:path';
import { TestSessionOptions } from '@salesforce/cli-plugins-testkit/lib/testSession.js';
import { registry } from '@salesforce/source-deploy-retrieve';
import { assert } from 'chai';

export const SOURCE_BASE_RELATIVE_PATH = join('force-app', 'main', 'default');
export const DEB_WEBAPP_NUTS_PATH = join(process.cwd(), 'test', 'nuts', 'digitalExperienceBundleWithWebapps');

export const TYPES = {
DEB: registry.types.digitalexperiencebundle,
} as const;

export const DIR_NAMES = {
PROJECT: 'project',
DIGITAL_EXPERIENCES: TYPES.DEB.directoryName,
WEB_APP: 'web_app',
WEBAPP_NAME: 'WebApp',
} as const;

assert(DIR_NAMES.DIGITAL_EXPERIENCES);

export const WEBAPPS_RELATIVE_PATH = join(SOURCE_BASE_RELATIVE_PATH, DIR_NAMES.DIGITAL_EXPERIENCES, DIR_NAMES.WEB_APP);
export const WEBAPP_RELATIVE_PATH = join(WEBAPPS_RELATIVE_PATH, DIR_NAMES.WEBAPP_NAME);

export const FULL_NAMES = {
WEBAPP: `${DIR_NAMES.WEB_APP}/${DIR_NAMES.WEBAPP_NAME}`,
} as const;

export const METADATA = {
WEBAPP: `${TYPES.DEB.name}:${FULL_NAMES.WEBAPP}`,
ALL_DEBS: TYPES.DEB.name,
};

export const TEST_SESSION_OPTIONS: TestSessionOptions = {
project: {
sourceDir: join(DEB_WEBAPP_NUTS_PATH, DIR_NAMES.PROJECT),
},
devhubAuthStrategy: 'AUTO',
scratchOrgs: [
{
setDefault: true,
config: join('config', 'project-scratch-def.json'),
},
],
};
46 changes: 46 additions & 0 deletions test/nuts/digitalExperienceBundleWithWebapps/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2025, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { join } from 'node:path';
import * as fs from 'node:fs';
import { expect } from 'chai';
import { WEBAPP_RELATIVE_PATH } from './constants.js';

/**
* Delete local webapp source
*/
export async function deleteLocalSource(sourceRelativePath: string, projectDir: string): Promise<void> {
const fullPath = join(projectDir, sourceRelativePath);
if (fs.existsSync(fullPath)) {
await fs.promises.rm(fullPath, { recursive: true });
await fs.promises.mkdir(fullPath, { recursive: true });
}
}

/**
* Convert metadata string to array format for CLI commands
*/
export const metadataToArray = (metadata: string): string => `--metadata ${metadata.split(',').join(' --metadata ')}`;

/**
* Verify webapp files exist locally after retrieve
*/
export function assertWebAppFilesExist(projectDir: string): void {
const webappPath = join(projectDir, WEBAPP_RELATIVE_PATH);
expect(fs.existsSync(webappPath), `WebApp directory should exist at ${webappPath}`).to.be.true;
expect(fs.existsSync(join(webappPath, 'webapp.json')), 'webapp.json should exist').to.be.true;
expect(fs.existsSync(join(webappPath, 'src', 'App.js')), 'src/App.js should exist').to.be.true;
}
21 changes: 21 additions & 0 deletions test/nuts/digitalExperienceBundleWithWebapps/project/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Salesforce DX Project: Next Steps

Now that you've created a Salesforce DX project, what's next? Here are some documentation resources to get you started.

## How Do You Plan to Deploy Your Changes?

Do you want to deploy a set of changes, or create a self-contained application? Choose
a [development model](https://developer.salesforce.com/tools/vscode/en/user-guide/development-models).

## Configure Your Salesforce DX Project

The `sfdx-project.json` file contains useful configuration information for your project.
See [Salesforce DX Project Configuration](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_ws_config.htm)
in the _Salesforce DX Developer Guide_ for details about this file.

## Read All About It

- [Salesforce Extensions Documentation](https://developer.salesforce.com/tools/vscode/)
- [Salesforce CLI Setup Guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm)
- [Salesforce DX Developer Guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_intro.htm)
- [Salesforce CLI Command Reference](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference.htm)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"orgName": "DEB Webapp NUT Org",
"edition": "Developer",
"features": ["EnableSetPasswordInApi", "Communities"],
"settings": {
"lightningExperienceSettings": {
"enableS1DesktopEnabled": true
},
"mobileSettings": {
"enableS1EncryptedStoragePref2": false
},
"experienceBundleSettings": {
"enableExperienceBundleMetadata": true
},
"communitiesSettings": {
"enableNetworksEnabled": true
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web App 2 - React Application" />
<title>WebApp2</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.App {
text-align: center;
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}

.card {
padding: 2rem;
background-color: #1a1d23;
border-radius: 8px;
margin-top: 2rem;
max-width: 600px;
}

.card h2 {
margin-top: 0;
color: #61dafb;
}

code {
background-color: #1a1d23;
padding: 0.2rem 0.4rem;
border-radius: 4px;
font-family: 'Courier New', monospace;
color: #61dafb;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import './App.css';

function App() {
return (
<div className="App">
<header className="App-header">
<h1>Welcome to WebApp2</h1>
<p>This is a dummy React application.</p>
<div className="card">
<h2>Getting Started</h2>
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
</div>
</header>
</div>
);
}

export default App;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"outputDir": "dist",
"apiVersion": "67.0",
"i18n": {
"defaultLocale": "en-US",
"locales": ["en-US"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"packageDirectories": [
{
"path": "force-app",
"default": true
}
],
"name": "digitalExperienceBundleWithWebapps",
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "67.0"
}
Loading