
1. Overview
In a typical testing workflow, creating presentation slides for test reports (e.g., test summaries, module status, KPI reports) is often time-consuming due to:
- Manually copying slide templates
- Entering test data for each slide
- Adjusting layout and formatting
To solve this problem, I implemented an automated solution:
Generate Google Slides from Google Sheets using Google Apps Script with template and placeholder replacement
This approach allows:
- Generating 10+ slides automatically
- Synchronizing real test data from Google Sheets
- Reducing manual effort and human error
2. Example Data & Template (Real Case)
📊 Google Sheets (Data Source)
The dataset used:
| TITLE | MODULE_NAME | TOTAL_CASES | PASSED | FAILED | TESTER_NAME | STATUS_COLOR |
|---|---|---|---|---|---|---|
| Login Security Test | Authentication | 45 | 42 | 3 | Nguyễn Văn A | Red |
| Dashboard UI Check | User Interface | 30 | 30 | 0 | Trần Thị B | Green |
| Checkout Flow Test | E-commerce | 60 | 55 | 5 | Lê Văn C | Red |
👉 Each row represents one slide
🖥️ Google Slides Template
Slide 1 (Title slide)
{{TITLE}}{{MODULE_NAME}} | {{TESTER_NAME}}
Slide 2 (Detail slide)
{{MODULE_NAME}}Tổng số case: {{TOTAL_CASES}}
Số case pass: {{PASSED}}
Số case fail: {{FAILED}}
👉 Placeholders are customized based on real testing data instead of generic ones like {{POINT1}}.
3. Solution Architecture
- Google Sheets → Data source
- Google Slides Template → Predefined layout with placeholders
- Google Apps Script → Automation engine
4. Folder Setup (Reproducible)
Prepare:
Template Presentation ID: YOUR_TEMPLATE_ID
Output Folder ID: YOUR_FOLDER_ID
👉 These IDs ensure the solution is reusable and scalable.
5. Full Apps Script Code
const CONFIG = {
TEMPLATE_ID: '1PenP76oNotkbbZ58yJMrH-OebQ_8GJeh2XCAIx4ZZcE',
OUTPUT_FOLDER_ID: '1cyXmz30_xRcbjo0ZQQozHWH79XD3WrIu',
SHEET_NAME: 'Data' // Đảm bảo tên Sheet chứa dữ liệu của bạn đang là 'Data'
};
function generateBulkSlides() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(CONFIG.SHEET_NAME);
const dataRange = sheet.getDataRange();
const data = dataRange.getValues();
data.shift();
const outputFolder = DriveApp.getFolderById(CONFIG.OUTPUT_FOLDER_ID);
const templateFile = DriveApp.getFileById(CONFIG.TEMPLATE_ID);
let tempSheet = ss.getSheetByName("TEMP_CHART");
if (!tempSheet) {
tempSheet = ss.insertSheet("TEMP_CHART");
}
data.forEach((row, index) => {
if (!row[0]) return;
const presentationName = `Report_${row[1]}_${new Date().toISOString().split('T')[0]}`;
const newFile = templateFile.makeCopy(presentationName, outputFolder);
const slideDeck = SlidesApp.openById(newFile.getId());
// 👉 Mapping mới theo sheet của bạn
const mapData = {
'{{TITLE}}': row[0] || '',
'{{MODULE_NAME}}': row[1] || '',
'{{TOTAL_CASES}}': row[2] || 0,
'{{PASSED}}': row[3] || 0,
'{{FAILED}}': row[4] || 0,
'{{TESTER_NAME}}': row[5] || '',
'{{STATUS_COLOR}}': row[6] || ''
};
for (const [placeholder, value] of Object.entries(mapData)) {
slideDeck.replaceAllText(placeholder, value.toString());
}
// 👉 Tạo chart mới (Passed vs Failed)
tempSheet.clear();
const chartData = [
["Status", "Count"],
["Passed", row[3] || 0],
["Failed", row[4] || 0],
];
tempSheet.getRange(1, 1, chartData.length, 2).setValues(chartData);
const chartBuilder = tempSheet.newChart()
.setChartType(Charts.ChartType.PIE) // 👈 đổi sang pie cho dễ nhìn
.addRange(tempSheet.getRange("A1:B3"))
.setOption('title', `Test Result - ${row[1]}`)
.setPosition(1, 1, 0, 0)
.build();
tempSheet.insertChart(chartBuilder);
const tempCharts = tempSheet.getCharts();
const currentChart = tempCharts[tempCharts.length - 1];
const slides = slideDeck.getSlides();
if (slides.length >= 2) {
const targetSlide = slides[1];
targetSlide.insertImage(
currentChart.getBlob(),
250, 50, 450, 300
);
}
tempSheet.removeChart(currentChart);
slideDeck.saveAndClose();
Logger.log(`Đã tạo thành công: ${presentationName}`);
});
}
6. Key Design Considerations
🔹 Data Mapping Design
- Mapping is based on column headers:
{{TITLE}} → TITLE column
{{MODULE_NAME}} → MODULE_NAME column
{{PASSED}} → PASSED column
👉 Advantage:
- No hardcoding
- Easily scalable
🔹 Placeholder Naming Rules
- Format:
{{NAME}} - Must exactly match column headers
- Case-sensitive
🔹 Handling Missing Data
const value = row[i] || "N/A";
👉 Ensures:
- No script failure
- Clear fallback value
7. Generating 10+ Slides
- Each row = 1 presentation
- With 10 rows → 10 slide files generated automatically
8. KPI Table Integration (Requirement)
In addition to text replacement, the script:
- Dynamically inserts a KPI table
- Uses real data (TOTAL_CASES, PASSED, FAILED)
👉 This satisfies:
9. Output Result
After execution:
- Files are generated in the output folder:
Test_Report_1
Test_Report_2
...
Each presentation includes:
- Slide 1: Title + Module + Tester
- Slide 2: KPI details + table
🎥 Demo Video: https://youtu.be/RJaaWxTkytM
10. Practical Use Case (Testing)
This solution can be applied to:
- Test summary reports
- Weekly QA reports
- Client demo presentations
Benefits:
- Saves significant time
- Reduces manual errors
- Improves consistency
11. Conclusion
By combining:
- Google Sheets
- Google Slides
- Google Apps Script
👉 The slide creation process can be fully automated.
Instead of manually creating slides, you only need to update data in Google Sheets and run the script.