Skip to content

Improve setup script with enhanced visual output and comprehensive testing #289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
129 changes: 129 additions & 0 deletions __tests__/setup.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* Tests for the enhanced setup script
*/

const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

describe('Setup Script', () => {
const setupScriptPath = path.join(__dirname, '..', 'script', 'setup');

beforeAll(() => {
// Ensure the setup script exists and is executable
expect(fs.existsSync(setupScriptPath)).toBe(true);
});

test('setup script should be executable', () => {
const stats = fs.statSync(setupScriptPath);
// Check if the file has executable permission
// eslint-disable-next-line no-bitwise
expect(stats.mode & 0o111).not.toBe(0);
});

test('setup script should contain progress functions', () => {
const content = fs.readFileSync(setupScriptPath, 'utf8');

// Check for new progress functions
expect(content).toContain('progress_header()');
expect(content).toContain('step_start()');
expect(content).toContain('step_success()');
expect(content).toContain('step_warning()');
expect(content).toContain('step_error()');
expect(content).toContain('step_info()');
expect(content).toContain('show_progress()');
expect(content).toContain('final_summary()');
});

test('setup script should contain color definitions', () => {
const content = fs.readFileSync(setupScriptPath, 'utf8');

// Check for color definitions
expect(content).toContain('RED=');
expect(content).toContain('GREEN=');
expect(content).toContain('YELLOW=');
expect(content).toContain('BLUE=');
expect(content).toContain('CYAN=');
expect(content).toContain('NC=');
});

test('setup script should contain progress tracking variables', () => {
const content = fs.readFileSync(setupScriptPath, 'utf8');

// Check for progress tracking
expect(content).toContain('TOTAL_STEPS=8');
expect(content).toContain('CURRENT_STEP=0');
expect(content).toContain('START_TIME=');
});

test('setup script should have proper step structure', () => {
const content = fs.readFileSync(setupScriptPath, 'utf8');

// Check for all steps
expect(content).toContain('Step 1: Xcode Tools');
expect(content).toContain('Step 2: Homebrew');
expect(content).toContain('Step 3: Ruby Environment');
expect(content).toContain('Step 4: Node.js Environment');
expect(content).toContain('Step 5: Yarn Package Manager');
expect(content).toContain('Step 6: Project Dependencies');
expect(content).toContain('Step 7: iOS Dependencies');
expect(content).toContain('Step 8: Final Setup');
});

test('setup script should have enhanced user feedback', () => {
const content = fs.readFileSync(setupScriptPath, 'utf8');

// Check for enhanced feedback
expect(content).toContain('✅'); // Success checkmarks
expect(content).toContain('⚠️'); // Warnings
expect(content).toContain('❌'); // Errors
expect(content).toContain('ℹ️'); // Info
expect(content).toContain('🎉'); // Celebration
expect(content).toContain('⏱️'); // Timer
});

test('setup script should contain progress bar functionality', () => {
const content = fs.readFileSync(setupScriptPath, 'utf8');

// Check for progress bar elements
expect(content).toContain('Progress: [');
expect(content).toContain('█'); // Filled progress
expect(content).toContain('░'); // Empty progress
expect(content).toContain('percentage');
});

test('setup script should have timing functionality', () => {
const content = fs.readFileSync(setupScriptPath, 'utf8');

// Check for timing
expect(content).toContain('START_TIME=$(date +%s)');
expect(content).toContain('end_time=$(date +%s)');
expect(content).toContain('duration=');
expect(content).toContain('Total setup time:');
});

test('setup script should maintain all original functionality', () => {
const content = fs.readFileSync(setupScriptPath, 'utf8');

// Check that all original functionality is preserved
expect(content).toContain('xcode-select');
expect(content).toContain('brew');
expect(content).toContain('rbenv');
expect(content).toContain('nvm');
expect(content).toContain('yarn');
expect(content).toContain('pod install');
});

test('setup script should start with proper shebang', () => {
const content = fs.readFileSync(setupScriptPath, 'utf8');
expect(content.startsWith('#!/bin/sh')).toBe(true);
});

test('setup script dry run should not fail on syntax', () => {
// Test that the script doesn't have syntax errors
// Note: This uses bash -n to check syntax without executing
expect(() => {
execSync(`bash -n "${setupScriptPath}"`, { stdio: 'pipe' });
}).not.toThrow();
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"!**/__mocks__"
],
"scripts": {
"test": "echo test",
"test": "jest",
"typescript": "tsc --noEmit",
"lint": "eslint \"**/*.{js,ts,tsx}\"",
"prepare": "bob build",
Expand Down
161 changes: 133 additions & 28 deletions script/setup
Original file line number Diff line number Diff line change
@@ -1,84 +1,189 @@
#!/bin/sh

#
# Enhanced Setup Script with Progress Indicators
#

# Colors for better output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# Progress tracking
TOTAL_STEPS=8
CURRENT_STEP=0
START_TIME=$(date +%s)

#
# Functions
#
function log() {
echo "\n"
echo "======================================="
echo "$1"
echo "======================================="
function progress_header() {
echo "\n${CYAN}┌─────────────────────────────────────────────────────────────┐${NC}"
echo "${CYAN}│ 🏎️ Intercom React Native Setup Script 🏎️ │${NC}"
echo "${CYAN}└─────────────────────────────────────────────────────────────┘${NC}"
echo "${BLUE}Setting up your development environment...${NC}\n"
}

log "🏎️ Running React native setup script 🏎️"
function step_start() {
CURRENT_STEP=$((CURRENT_STEP + 1))
local step_name="$1"
local icon="$2"

echo "${CYAN}┌─ Step ${CURRENT_STEP}/${TOTAL_STEPS} ────────────────────────────────────────────────┐${NC}"
echo "${CYAN}│${NC} ${icon} ${step_name}"
echo "${CYAN}└─────────────────────────────────────────────────────────────┘${NC}"
}

log "📱 Installing xcode tools 📱"
function step_success() {
local message="$1"
echo "${GREEN}✅ ${message}${NC}"
echo ""
}

function step_warning() {
local message="$1"
echo "${YELLOW}⚠️ ${message}${NC}"
}

function step_error() {
local message="$1"
echo "${RED}❌ ${message}${NC}"
}

function step_info() {
local message="$1"
echo "${BLUE}ℹ️ ${message}${NC}"
}

function show_progress() {
local percentage=$((CURRENT_STEP * 100 / TOTAL_STEPS))
local filled=$((CURRENT_STEP * 50 / TOTAL_STEPS))
local empty=$((50 - filled))

printf "${CYAN}Progress: [${NC}"
printf "%${filled}s" | tr ' ' '█'
printf "%${empty}s" | tr ' ' '░'
printf "${CYAN}] ${percentage}%%${NC}\n"
}

function final_summary() {
local end_time=$(date +%s)
local duration=$((end_time - START_TIME))
local minutes=$((duration / 60))
local seconds=$((duration % 60))

echo "\n${GREEN}┌─────────────────────────────────────────────────────────────┐${NC}"
echo "${GREEN}│ 🎉 Setup Complete! 🎉 │${NC}"
echo "${GREEN}└─────────────────────────────────────────────────────────────┘${NC}"
echo "${GREEN}✅ All dependencies installed successfully${NC}"
printf "${BLUE}⏱️ Total setup time: %dm %ds${NC}\n" "$minutes" "$seconds"
echo "\n${CYAN}Next steps:${NC}"
echo "${GREEN}📱 Run example app on iOS: ${YELLOW}yarn example ios${NC}"
echo "${GREEN}📱 Run example app on Android: ${YELLOW}yarn example android${NC}"
}

progress_header

# Step 1: Xcode Tools
step_start "Checking Xcode Command Line Tools" "🛠️"
if [ ! -f /Library/Developer/CommandLineTools/usr/lib/libxcrun.dylib ]; then
echo "⚠️ Xcode CommandLineTools not found installing. Please install and rerun this script ⚠️"
step_warning "Xcode CommandLineTools not found. Installing..."
step_info "Please complete the Xcode installation and rerun this script"
xcode-select --install
exit 1
fi
echo "Xcode CommandLineTools installed 👍"

if ! [ -x "$(command -v xcode-select)" ]; then
echo "⚠️ You need Xcode to setup this project. Please install and rerun this script ⚠️"
step_error "Xcode is required to setup this project. Please install and rerun this script"
exit 1
fi

log "👀 Looking for Homebrew 👀"
step_success "Xcode Command Line Tools are installed"
show_progress

# Step 2: Homebrew
step_start "Setting up Homebrew package manager" "🍺"
if ! [ -x "$(command -v brew)" ]; then
echo "🍺 Installing Homebrew 🍺"
step_info "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo 'export PATH=/opt/homebrew/bin:$PATH' >> ~/.bash_profile
echo 'export PATH=/opt/homebrew/bin:$PATH' >> ~/.zshrc
step_success "Homebrew installed successfully"
else
echo "Homebrew already installed 👍"
step_success "Homebrew is already installed"
fi
show_progress

log "👀 Looking for rbenv 👀"
# Step 3: Ruby Environment (rbenv)
step_start "Setting up Ruby environment with rbenv" "💎"
if ! [ -x "$(command -v rbenv)" ]; then
echo "🍺 Installing rbenv with brew 🍺"
step_info "Installing rbenv and ruby-build..."
brew install rbenv ruby-build
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.zshrc
eval "$(rbenv init -)"
step_success "rbenv installed successfully"
else
echo "rbenv already installed 👍"
step_success "rbenv is already installed"
fi

step_info "Setting up Ruby version..."
rbenv install --skip-existing
echo "Ruby setup complete 👍"
step_success "Ruby environment configured"
show_progress

log "👀 Looking for nvm 👀"
# Step 4: Node.js Environment (nvm)
step_start "Setting up Node.js environment with nvm" "📗"
if ! [ -x "$(command -v nvm)" ]; then
echo "🍺 Installing nvm with brew 🍺"
step_info "Installing nvm..."
brew install nvm
step_success "nvm installed successfully"
else
step_success "nvm is already installed"
fi

# Source nvm
export NVM_DIR="$HOME/.nvm"
[ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh"

step_info "Installing Node.js version from .nvmrc..."
nvm install
echo "Node.js setup complete 👍"
step_success "Node.js environment configured"
show_progress

log "👀 Looking for yarn 👀"
# Step 5: Yarn Package Manager
step_start "Setting up Yarn package manager" "🧶"
if ! [ -x "$(command -v yarn)" ]; then
echo "🍺 Installing yarn 🍺"
step_info "Installing Yarn..."
brew install yarn
step_success "Yarn installed successfully"
else
echo "yarn already installed 👍"
step_success "Yarn is already installed"
fi
show_progress

log "📟 installing dependencies 📟"
# Step 6: Project Dependencies
step_start "Installing project dependencies" "📦"
step_info "Running yarn install..."
yarn
step_success "Project dependencies installed"
show_progress

log "📟 installing dependencies for our example app 📟"
# Step 7: iOS Dependencies
step_start "Installing iOS dependencies" "📱"
step_info "Installing CocoaPods dependencies..."
cd example/ios
pod install
cd -
step_success "iOS dependencies installed"
show_progress

# Step 8: Final Setup
step_start "Finalizing setup" "🏁"
step_success "Setup completed successfully"
show_progress

echo "You're all set up 👍"
echo "📱 Run example app on iOS using -> yarn example ios"
echo "📱 Run example app on android using -> yarn example android"
final_summary